scapy yield的含義和使用

2021-09-21 03:50:30 字數 3804 閱讀 5321

scapy的實現中,yield的用法很好,它使得loop成為乙個生成器,從而使得__iter__返回乙個迭代器,那麼yield的本質是什麼呢?

保有yield的函式返回的是乙個迭代器,而返回迭代器的也就是生成器了,用yield構造迭代器將會非常方便,總的來說,設yield函式返回乙個迭代器iter1,只有在你顯式的呼叫其next函式或者隱式作for-in操作時,yield函式中的yield值才會依次按照其yield的順序返回出來,yield函式如果你使用yield n,那麼這和return n是區別市很大的,如果僅僅return n的話,這個n就直接被返回了,它僅僅是乙個值,而如果是yield n的話,雖然最終還是可以得到的還是n,但是你得到n的方式卻變了,你只能通過iter的介面來得到n。yield只是在「塞」住了很多資料,只有iter的介面才能將其乙個乙個「拔」出來,在乙個函式中,只要你yield了乙個資料,那麼就等於塞住了乙個資料,將來需要用iter介面拔出它,比如以下的例子:

def test():

print "333"

yield 3

print "444"

yield 4

print "555"

yield 5

使用命令列執行之:

>>> def test():

...  print "333"

...  yield 3

...  print "444"

...  yield 4

...  print "555"

...  yield 5

... 

>>> it=test() #沒有任何輸出

>>> a=it.next() #a的值是3,並且將輸出333,後面的444,555依舊不輸出,必須等待下次呼叫next以及下下次呼叫

333>>> print a

3隱式呼叫也一樣:

>>> it=test()

>>> for k in it:

...  print k

...  break

... 

3333

>>> it=test()

>>> for r in it:

...  print r

... 

4444

5555

就是這樣!有了yield的理論知識,接下來再看scapy的packet類的__iter__這個函式:

00def __iter__(self):

01    def loop(todo, done, self=self):

02      if todo:

03        eltname = todo.pop()  

04        elt = self.__getattr__(eltname)

05        if not isinstance(elt,  gen):

06          if  self.fieldtype[eltname].islist:

07            elt = setgen([elt])

08          else:

09            elt = setgen(elt)

10        for e in elt:

11          done[eltname] = e

12          for x in loop(todo[:], done):

13            yield x  

14      else:

15        if isinstance(self.payload,nopayload):

16          payloads = [none]

17        else:

18          payloads = self.payload

19          for payl in payloads:

20            done2 = done.copy()

21            for k in done2:

22              if isinstance(done2[k], randnum):

23                done2[k] = int(done2[k])

24              pkt = self.__class__(**done2)

25              pkt.underlayer = self.underlayer

26              pkt.overload_fields = self.overload_fields.copy()

27              if payl is none:

28                yield pkt

29              else:

30                yield pkt/payl  

31  return loop(map(lambda x:str(x), self.fields.keys()), {}) 

在__str__中呼叫__iter__().next()返回的packet實際上只有乙個,那就是在13行返回的這個x,這是為何呢?在__iter__中總共有三個地方「塞」住了packet(事實上可以歸為兩個,因為28行和30行可以視為乙個),分別在13行,28行和30行,在__iter__的執行過程中,首先進入的是前半部分,只有在todo沒有的時候才會進入後半部分else,可見if todo段是解決本層packet物件的,如果todo沒有了,在12行呼叫loop時才會進入到else段,因此else段是遞迴解決本層packet物件的payload的,然後在28行或者30行「塞」乙個packet,可是如果執行到了28行或者30行,也塞住了packet,那麼接下來返回到**呢?想象一下當初是怎麼進來的,是從12行進來的,於是返回12行,返回之後,直接就被「拔」出了,這是因為12行有乙個for-in,拔出28行或者 30行塞入的packet後緊接著又塞入乙個,然後如果該層packet物件的屬性(也即todo鍊錶)不止乙個,還會進一步的返回上乙個todo屬性呼叫的loop,在for-in中又把剛剛在13行塞入的packet給拔出了,最終__iter__返回的時候,其實只有最後乙個packet物件在13行被塞入。

對於直接呼叫__str__進而呼叫__iter__的packet物件而言,進入else之後28行或者30行的yield被12行的for-in所抵消,最終在13行yield乙個packet物件,也是唯一的乙個,對於進入19行的for-in而間接遞迴呼叫__iter__的所取得的payl這個packet物件而言,其在13行最終yield的那個唯一的packet物件被19行本身的for-in所抵消,這樣最後就剩下了直接呼叫__str__函式的那個packet物件本身。str函式的不斷呼叫使得包的構建從下往上進行,每次上公升一層,因為每次都會以已經處理完的packet物件的payload再次呼叫str,從l3packetsocket的send函式的outs.send中的str開始這一過程,隨後在do_build中的p+str(self.payload)中繼續這一過程,完成包的構建。

13行的yield x返回兩個地方,乙個是直接的__iter__().next()的呼叫,比如__str__中,另乙個是隱式的for-in呼叫,其中也類似乙個next的呼叫,比如19行的for-in,完全和13行的yield x「塞」「拔」抵消,另外12行的for-in,也是完全抵消,然而緊接著在13行又「塞」了乙個,這就構成了乙個迴圈,乙個遞迴的迴圈。12-13行的「塞拔」是塞拔的同乙個packet物件,先拔再塞,拔的是28/29行塞入的物件或者13行塞入的物件,19行拔的是當前packet物件的payload,而這個payload是在遞迴到上一層時在13行最後塞入的。懵了嗎?遞迴加迭代就是這麼...

scapy yield的含義和使用

scapy的實現中,yield的用法很好,它使得loop成為乙個生成器,從而使得 iter 返回乙個迭代器,那麼yield的本質是什麼呢?保有yield的函式返回的是乙個迭代器,而返回迭代器的也就是生成器了,用yield構造迭代器將會非常方便,總的來說,設yield函式返回乙個迭代器iter1,只有...

快取頭 Cache Control 的含義和使用

本篇我們來寫一下http 中的快取,即cache control cache control 的可快取性 指明哪些地方可以快取返回的資料 public http 返回的時候在heaher 中設定cache control 的值為 public 它代表,這個http 請求它返回的內容所經過的任何路徑中...

快取頭Cache Control 的含義和使用

1 public 表示任何地方 都可以進行快取 2 private 表示 只允許 發起 請求 的瀏覽器 才可以 進行 快取 3 no cache 任何 地方 都不允許 快取 可以進行快取,但是每次都需要向伺服器請求驗證 快取是否可用 4 max age 單位s 秒 表示 快取 過多久 時間 到期 5...