Python 迭代器與生成器

https://zhuanlan.zhihu.com/p/26123333

要完全理解透生成器,需要我們先掌握三個(gè)概念:

可迭代對(duì)象(Iterable)

迭代器(Iterator)

迭代(Iteration)

放一張圖來(lái)理解浇借,來(lái)自這里

額外提到了容器(container)论矾,說(shuō)的是我們的集合類對(duì)象,如 list沛简、set齐鲤、dict斥废,它們將多個(gè)元素組織在一起,這些對(duì)象就可以稱為 container给郊。

可迭代對(duì)象:

可直接作用于for循環(huán)的對(duì)象統(tǒng)稱為Iterable 牡肉。具體的實(shí)現(xiàn)是,Python 中的對(duì)象只要定義了__iter__方法(該方法返回一個(gè)迭代器對(duì)象)淆九,或者定義了支持下標(biāo)索引的__getitem__方法统锤,那么這個(gè)對(duì)象就是可迭代對(duì)象。

>>>fromcollectionsimportIterable>>>isinstance([],Iterable)True>>>isinstance({},Iterable)True>>>isinstance([xforxinrange(10)],Iterable)True

迭代器:

可作用于next()函數(shù)的對(duì)象都是Iterator炭庙。具體的實(shí)現(xiàn)是饲窿,任何對(duì)象只要定義了__iter__和__next__方法,那就是迭代器對(duì)象焕蹄;迭代器表示一個(gè)惰性計(jì)算的序列逾雄,需要__iter__返回迭代器自身,__next__返回迭代器中的下一個(gè)值腻脏,迭代到結(jié)尾時(shí)引發(fā)

StopIteration

異常鸦泳;也就是說(shuō)迭代器在遍歷集合時(shí),并不是將所有的元素事先都準(zhǔn)備好永品,而是迭代到某個(gè)元素時(shí)才去計(jì)算該元素做鹰,利用這一特性我們可以去遍歷一些巨大的集合,之前總結(jié)的函數(shù)式編程中鼎姐,map钾麸,reduce,filter函數(shù)返回的就是一個(gè)新的迭代器炕桨。

還有一點(diǎn)需要明確的饭尝,迭代器都是可迭代對(duì)象,可迭代對(duì)象可以通過(guò)iter()返回一個(gè)新的迭代器谋作。

>>>L=[1,2,3,4,5]>>>'__iter__'indir(L)True>>>'__next__'indir(L)False>>>newL=iter(L)>>>'__next__'indir(newL)True>>>newL.__next__()1>>>newL.__next__()2# 定義斐波拉契數(shù)的迭代器>>>classfib(object):...def__init__(self):...self.prev=0...self.curr=1...def__iter__(self):...returnself...def__next__(self):...value=self.curr...self.curr+=self.prev...self.prev=value...returnvalue...>>>f=fib()>>>foriinf:...ifi>20:break...print(i)...11235813

從上面的迭代操作中芋肠,可以看出 for 循環(huán)其實(shí)是調(diào)用__iter__獲得迭代器,再調(diào)用__next__獲取元素遵蚜,迭代器內(nèi)部狀態(tài)保存在當(dāng)前實(shí)例對(duì)象的prev以及cur屬性中帖池,在下一次調(diào)用中將使用這兩個(gè)屬性。每次調(diào)用next()方法都會(huì)執(zhí)行以下兩步操作:

修改狀態(tài)吭净,以便下次調(diào)用next()方法

計(jì)算當(dāng)前調(diào)用的結(jié)果

迭代器的使用非常普通睡汹,Python的內(nèi)置庫(kù)itertools就是專門返回迭代器對(duì)象的,這篇博文專門介紹itertools庫(kù)的寂殉,我從中列舉了一些:

# 累加>>>importitertools>>>a=itertools.accumulate(range(10))>>>a>>>print(list(a))[0,1,3,6,10,15,21,28,36,45]# 連接列表或迭代器>>>c=itertools.chain(range(3),range(4),[0,1,2,3,4])>>>print(list(c))[0,1,2,0,1,2,3,0,1,2,3,4]# 按照真值表篩選元素>>>x=itertools.compress(range(5),(True,False,True,True,False))>>>print(list(x))[0,2,3]# 計(jì)數(shù)器,可以指定起始位置和步長(zhǎng)>>>x=itertools.count(start=20,step=-1)>>>print(list(itertools.islice(x,0,10,1)))[20,19,18,17,16,15,14,13,12,11]# 按照分組函數(shù)的值對(duì)元素進(jìn)行分組>>>x=itertools.groupby(range(10),lambdax:x<5orx>8)>>>forcondition,numbersinx:...print(condition,list(numbers))True[0,1,2,3,4]False[5,6,7,8]True[9]# 類似map>>>x=itertools.starmap(str.islower,'aBCDefGhI')>>>print(list(x))[True,False,False,False,True,True,False,True,False]

生成器:

有了前面的鋪墊囚巴,我們就能更好地理解生成器了。生成器是什么?說(shuō)白了生成器就是一種特殊的迭代器彤叉,不過(guò)它的實(shí)現(xiàn)方式更為簡(jiǎn)單優(yōu)雅庶柿,同樣我們可以明確的是,任何生成器都是迭代器秽浇,生成器也是一個(gè)惰性計(jì)算的序列浮庐。

我們來(lái)看看生成器的兩種定義方式:

1、生成器表達(dá)式:

>>>[i*iforiinrange(5)]# 注意 Python3 中 range函數(shù)是迭代器[0,1,4,9,16]# 根據(jù)列表生成式柬焕,只需要簡(jiǎn)單修改就可以定義生成器>>>(i*iforiinrange(3))at0x7f59ed8fc3b8>

2审残、另一種定義復(fù)雜推導(dǎo)算法的生成器需要引入一個(gè)強(qiáng)大的關(guān)鍵字yield:

# 斐波那契序列的生成器函數(shù)>>> def fib():...? ? prev = curr = 1...? ? yield prev #1...? ? yield curr #2...? ? while True:...? ? ? ? prev, curr = curr, prev + curr...? ? ? ? yield curr... >>> f = fib()>>> f>>> for i in f:? ? # 還可以使用 next() 遍歷生成器...? ? if i > 20: break...? ? print(i)... 11235813

分析一下流程:

調(diào)用生成器函數(shù)時(shí)只返回一個(gè) generator 對(duì)象 f,函數(shù)并沒有執(zhí)行斑举;

通過(guò) for 循環(huán)生成器才開始執(zhí)行搅轿,執(zhí)行到 #1 yield prev 處,返回 yield 處的參數(shù) prev富玷,此時(shí)就打印出了1璧坟;

繼續(xù) for 循環(huán),生成器函數(shù)將在上一次停止的語(yǔ)句處繼續(xù)執(zhí)行凌彬,遇到 #2 yield curr 返回沸柔,此時(shí)又打印出了1循衰;

如此反復(fù)铲敛,直到i大于20跳出循環(huán)結(jié)束調(diào)用。

對(duì)比迭代器和生成器会钝,實(shí)現(xiàn)同樣的功能伐蒋,生成器會(huì)顯得更加優(yōu)雅簡(jiǎn)潔。

迭代

一句話總結(jié)迭代:按照一定的順序逐個(gè)訪問(wèn)容器中每一個(gè)元素的過(guò)程迁酸;也就是我們折騰斐波那契序列的過(guò)程

限于篇幅先鱼,生成器就介紹到這里,但生成器的威力遠(yuǎn)不止此奸鬓,下一篇將通過(guò)生成器和 yield 引出協(xié)程和異步IO等焙畔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市串远,隨后出現(xiàn)的幾起案子宏多,更是在濱河造成了極大的恐慌,老刑警劉巖澡罚,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伸但,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡留搔,警方通過(guò)查閱死者的電腦和手機(jī)更胖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人却妨,你說(shuō)我怎么就攤上這事饵逐。” “怎么了彪标?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵梳毙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我捐下,道長(zhǎng)账锹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任坷襟,我火速辦了婚禮奸柬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘婴程。我一直安慰自己廓奕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布档叔。 她就那樣靜靜地躺著桌粉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衙四。 梳的紋絲不亂的頭發(fā)上铃肯,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音传蹈,去河邊找鬼押逼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惦界,可吹牛的內(nèi)容都是我干的挑格。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼沾歪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼漂彤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起灾搏,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤挫望,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后确镊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體士骤,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年蕾域,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拷肌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片到旦。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖巨缘,靈堂內(nèi)的尸體忽然破棺而出添忘,到底是詐尸還是另有隱情,我是刑警寧澤若锁,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布搁骑,位于F島的核電站,受9級(jí)特大地震影響又固,放射性物質(zhì)發(fā)生泄漏仲器。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一仰冠、第九天 我趴在偏房一處隱蔽的房頂上張望乏冀。 院中可真熱鬧,春花似錦洋只、人聲如沸辆沦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肢扯。三九已至,卻和暖如春担锤,著一層夾襖步出監(jiān)牢的瞬間蔚晨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工妻献, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛛株,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓育拨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親欢摄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子熬丧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容