scapy-yield的含义和使用
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
...
333
3
>>> it=test()
>>> for r in it:
...? print r
...
444
4
555
5
就是這樣!有了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行最后塞入的。懵了嗎?遞歸加迭代就是這么...
轉載于:https://blog.51cto.com/dog250/1271133
總結
以上是生活随笔為你收集整理的scapy-yield的含义和使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解SET NAMES和mysql(
- 下一篇: WPF快速指导5:验证