scapy-yield的含义和使用
scapy的實(shí)現(xiàn)中,yield的用法很好,它使得loop成為一個(gè)生成器,從而使得__iter__返回一個(gè)迭代器,那么yield的本質(zhì)是什么呢?
保有yield的函數(shù)返回的是一個(gè)迭代器,而返回迭代器的也就是生成器了,用yield構(gòu)造迭代器將會(huì)非常方便,總的來說,設(shè)yield函數(shù)返回一個(gè)迭代器iter1,只有在你顯式的調(diào)用其next函數(shù)或者隱式作for-in操作時(shí),yield函數(shù)中的yield值才會(huì)依次按照其yield的順序返回出來,yield函數(shù)如果你使用yield N,那么這和return N是區(qū)別市很大的,如果僅僅return N的話,這個(gè)N就直接被返回了,它僅僅是一個(gè)值,而如果是yield N的話,雖然最終還是可以得到的還是N,但是你得到N的方式卻變了,你只能通過iter的接口來得到N。yield只是在“塞”住了很多數(shù)據(jù),只有iter的接口才能將其一個(gè)一個(gè)“拔”出來,在一個(gè)函數(shù)中,只要你yield了一個(gè)數(shù)據(jù),那么就等于塞住了一個(gè)數(shù)據(jù),將來需要用iter接口拔出它,比如以下的例子:
def test():
?print "333"
?yield 3
?print "444"
?yield 4
?print "555"
?yield 5
使用命令行運(yùn)行之:
>>> def test():
...? print "333"
...? yield 3
...? print "444"
...? yield 4
...? print "555"
...? yield 5
...
>>> it=test() #沒有任何輸出
>>> a=it.next() #a的值是3,并且將輸出333,后面的444,555依舊不輸出,必須等待下次調(diào)用next以及下下次調(diào)用
333
>>> print a
3
隱式調(diào)用也一樣:
>>> 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__這個(gè)函數(shù):
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__中調(diào)用__iter__().next()返回的Packet實(shí)際上只有一個(gè),那就是在13行返回的這個(gè)x,這是為何呢?在__iter__中總共有三個(gè)地方“塞”住了Packet(事實(shí)上可以歸為兩個(gè),因?yàn)?8行和30行可以視為一個(gè)),分別在13行,28行和30行,在__iter__的執(zhí)行過程中,首先進(jìn)入的是前半部分,只有在todo沒有的時(shí)候才會(huì)進(jìn)入后半部分else,可見if todo段是解決本層Packet對象的,如果todo沒有了,在12行調(diào)用loop時(shí)才會(huì)進(jìn)入到else段,因此else段是遞歸解決本層Packet對象的payload的,然后在28行或者30行“塞”一個(gè)Packet,可是如果執(zhí)行到了28行或者30行,也塞住了Packet,那么接下來返回到哪里呢?想象一下當(dāng)初是怎么進(jìn)來的,是從12行進(jìn)來的,于是返回12行,返回之后,直接就被“拔”出了,這是因?yàn)?2行有一個(gè)for-in,拔出28行或者 30行塞入的Packet后緊接著又塞入一個(gè),然后如果該層Packet對象的屬性(也即todo鏈表)不止一個(gè),還會(huì)進(jìn)一步的返回上一個(gè)todo屬性調(diào)用的loop,在for-in中又把剛剛在13行塞入的Packet給拔出了,最終__iter__返回的時(shí)候,其實(shí)只有最后一個(gè)Packet對象在13行被塞入。
???? 對于直接調(diào)用__str__進(jìn)而調(diào)用__iter__的Packet對象而言,進(jìn)入else之后28行或者30行的yield被12行的for-in所抵消,最終在13行yield一個(gè)Packet對象,也是唯一的一個(gè),對于進(jìn)入19行的for-in而間接遞歸調(diào)用__iter__的所取得的payl這個(gè)Packet對象而言,其在13行最終yield的那個(gè)唯一的Packet對象被19行本身的for-in所抵消,這樣最后就剩下了直接調(diào)用__str__函數(shù)的那個(gè)Packet對象本身。str函數(shù)的不斷調(diào)用使得包的構(gòu)建從下往上進(jìn)行,每次上升一層,因?yàn)槊看味紩?huì)以已經(jīng)處理完的Packet對象的payload再次調(diào)用str,從L3PacketSocket的send函數(shù)的outs.send中的str開始這一過程,隨后在do_build中的p+str(self.payload)中繼續(xù)這一過程,完成包的構(gòu)建。
???? 13行的yield x返回兩個(gè)地方,一個(gè)是直接的__iter__().next()的調(diào)用,比如__str__中,另一個(gè)是隱式的for-in調(diào)用,其中也類似一個(gè)next的調(diào)用,比如19行的for-in,完全和13行的yield x“塞”“拔”抵消,另外12行的for-in,也是完全抵消,然而緊接著在13行又“塞”了一個(gè),這就構(gòu)成了一個(gè)循環(huán),一個(gè)遞歸的循環(huán)。12-13行的“塞拔”是塞拔的同一個(gè)Packet對象,先拔再塞,拔的是28/29行塞入的對象或者13行塞入的對象,19行拔的是當(dāng)前Packet對象的payload,而這個(gè)payload是在遞歸到上一層時(shí)在13行最后塞入的。懵了嗎?遞歸加迭代就是這么...
轉(zhuǎn)載于:https://blog.51cto.com/dog250/1271133
總結(jié)
以上是生活随笔為你收集整理的scapy-yield的含义和使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解SET NAMES和mysql(
- 下一篇: 恢复从回收站中被删除的文件的方法