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