先說(shuō)迭代器,對(duì)于string、list、dict、tuple等這類(lèi)容器對(duì)象,使用for循環(huán)遍歷是很方便的。在后臺(tái)for語(yǔ)句對(duì)容器對(duì)象調(diào)用iter()函數(shù),iter()是python的內(nèi)置函數(shù)。iter()會(huì)返回一個(gè)定義了next()方法的迭代器對(duì)象,它在容器中逐個(gè)訪問(wèn)容器內(nèi)元素,next()也是python的內(nèi)置函數(shù)。在沒(méi)有后續(xù)元素時(shí),next()會(huì)拋出一個(gè)StopIteration異常,通知for語(yǔ)句循環(huán)結(jié)束。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> s = 'abc' >>> it = iter (s) >>> it <str_iterator object at 0x7f71fefe9d68 > >>> next (it) 'a' >>> next (it) 'b' >>> next (it) 'c' >>> next (it) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> StopIteration |
上面說(shuō)的都是python自帶的容器對(duì)象,它們都實(shí)現(xiàn)了相應(yīng)的迭代器方法,那如果是自定義類(lèi)需要遍歷怎么辦?方法很簡(jiǎn)單,對(duì)這個(gè)類(lèi)AClass,實(shí)現(xiàn)一個(gè)__iter__(self)方法,使其返回一個(gè)帶有__next__(self)方法的對(duì)象就可以了。如果你在AClass剛好也定義了__next__(self)方法(一般使用迭代器都會(huì)定義),那在__iter__里只要返回self就可以。廢話少說(shuō),先上代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Fib( object ): def __init__( self , max ): super (Fib, self ).__init__() self . max = max def __iter__( self ): self .a = 0 self .b = 1 return self def __next__( self ): fib = self .a if fib > self . max : raise StopIteration self .a, self .b = self .b, self .a + self .b return fib def main(): fib = Fib( 100 ) for i in fib: print (i) if __name__ = = '__main__' : main() |
簡(jiǎn)單講下代碼會(huì)干什么,定義了一個(gè)Fib類(lèi),用于生成fibonacci序列。用for遍歷時(shí)會(huì)逐個(gè)打印生成的fibonacci數(shù),max是生成的fibonacci序列中數(shù)字大小的上限。
在類(lèi)的實(shí)現(xiàn)中,定義了一個(gè)__iter__(self)方法,這個(gè)方法是在遍歷時(shí)被iter()調(diào)用,返回一個(gè)迭代器。因?yàn)樵诒闅v的時(shí)候,是直接調(diào)用的python內(nèi)置函數(shù)iter(),由iter()通過(guò)調(diào)用__iter__(self)獲得對(duì)象的迭代器。有了迭代器,就可以逐個(gè)遍歷元素了。而逐個(gè)遍歷的時(shí)候,也是使用內(nèi)置的next()函數(shù)通過(guò)調(diào)用對(duì)象的__next__(self)方法對(duì)迭代器對(duì)象進(jìn)行遍歷。所以要實(shí)現(xiàn)__iter__(self)和__next__(self)。而且因?yàn)閷?shí)現(xiàn)了__next__(self),所以在實(shí)現(xiàn)__iter__(self)的時(shí)候,直接返回self就可以。
為了更好理解,我再簡(jiǎn)單重復(fù)下上面說(shuō)的那一段:在循環(huán)遍歷自定義容器對(duì)象時(shí),會(huì)使用python內(nèi)置函數(shù)iter()調(diào)用遍歷對(duì)象的__iter__(self)獲得一個(gè)迭代器,之后再循環(huán)對(duì)這個(gè)迭代器使用next()調(diào)用迭代器對(duì)象的__next__(self)。__iter__只會(huì)被調(diào)用一次,而__next__會(huì)被調(diào)用 n 次。
下面說(shuō)生成器。
生成器(Generator)是創(chuàng)建迭代器的簡(jiǎn)單而強(qiáng)大的工具。它們寫(xiě)起來(lái)就像是正規(guī)的函數(shù),只是在需要返回?cái)?shù)據(jù)的時(shí)候使用yield語(yǔ)句。每次next()被調(diào)用時(shí),生成器會(huì)返回它脫離的位置(它記憶語(yǔ)句最后一次執(zhí)行的位置和所有的數(shù)據(jù)值)。以下示例演示了生成器可以很簡(jiǎn)單的創(chuàng)建出來(lái):
1
2
3
4
5
6
7
8
9
10
11
12
|
>>> def reverse(data): ... for index in range ( len (data) - 1 , - 1 , - 1 ): ... yield data[index] ... >>> for char in reverse( 'hello' ): ... print (char) ... o l l e h |
關(guān)于迭代器和生成器的區(qū)別,生成器能做到迭代器能做的所有事,而且因?yàn)樽詣?dòng)創(chuàng)建了__iter__()和 next()方法,生成器顯得特別簡(jiǎn)潔,而且生成器也是高效的。除了創(chuàng)建和保存程序狀態(tài)的自動(dòng)方法,當(dāng)發(fā)生器終結(jié)時(shí),還會(huì)自動(dòng)拋出StopIteration異常。一個(gè)帶有yield的函數(shù)就是一個(gè) 生成器,它和普通函數(shù)不同,生成一個(gè) generator 看起來(lái)像函數(shù)調(diào)用,但不會(huì)執(zhí)行任何函數(shù)代碼,直到對(duì)其調(diào)用next()(在 for 循環(huán)中會(huì)自動(dòng)調(diào)用next())才開(kāi)始執(zhí)行。雖然執(zhí)行流程仍按函數(shù)的流程執(zhí)行,但每執(zhí)行到一個(gè)yield語(yǔ)句就會(huì)中斷,并返回一個(gè)迭代值,下次執(zhí)行時(shí)從yield的下一個(gè)語(yǔ)句繼續(xù)執(zhí)行。看起來(lái)就好像一個(gè)函數(shù)在正常執(zhí)行的過(guò)程中被yield中斷了數(shù)次,每次中斷都會(huì)通過(guò)yield返回當(dāng)前的迭代值(yield暫停一個(gè)函數(shù),next()從其暫停處恢復(fù)其運(yùn)行)。
另外對(duì)于生成器,python還提供了一個(gè)生成器表達(dá)式:類(lèi)似與一個(gè)yield值的匿名函數(shù)。表達(dá)式本身看起來(lái)像列表推到, 但不是用方括號(hào)而是用圓括號(hào)包圍起來(lái):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>> unique_characters = { 'E' , 'D' , 'M' , 'O' , 'N' , 'S' , 'R' , 'Y' } >>> gen = ( ord (c) for c in unique_characters) >>> gen <generator object <genexpr> at 0x7f2be4668678 > >>> for i in gen: ... print (i) ... 69 79 83 77 82 78 89 68 >>> |
如果需要,可以將生成器表達(dá)式傳給tuple、list或是set來(lái)迭代所有的值并且返回元組、列表或是集合。在這種情況下,不需要一對(duì)額外的括號(hào) ———— 直接將生成器表達(dá)式 ord(c) for c in unique_characters傳給tuple()等函數(shù)就可以了, Python 會(huì)推斷出它是一個(gè)生成器表達(dá)式。
最后,為什么要使用生成器?因?yàn)樾省J褂蒙善鞅磉_(dá)式取代列表解析可以同時(shí)節(jié)省 cpu 和 內(nèi)存(ram)。如果你構(gòu)造一個(gè)列表的目的僅僅是傳遞給別的函數(shù),(比如 傳遞給tuple()或者set()), 那就用生成器表達(dá)式替代吧!
以上所述就是本文的全部?jī)?nèi)容了,希望大家能夠喜歡。