scapy yield的含義和使用

2021-08-25 15:49:13 字數 3491 閱讀 4082

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...