深入理解python迭代器和生成器(轉(zhuǎn)載)

轉(zhuǎn)載自深入講解Python中的迭代器和生成器

在Python中急膀,很多對(duì)象都是可以通過for語句來直接遍歷的,例如list龄捡、string卓嫂、dict等等,這些對(duì)象都可以被稱為可迭代對(duì)象聘殖。至于說哪些對(duì)象是可以被迭代訪問的晨雳,就要了解一下迭代器相關(guān)的知識(shí)了。

迭代器

一個(gè)對(duì)象存在迭代器對(duì)象奸腺,即要求這個(gè)對(duì)象是支持迭代器協(xié)議的對(duì)象餐禁,在Python中,支持迭代器協(xié)議就是實(shí)現(xiàn)對(duì)象的__iter__()和next()方法洋机。其中__iter__()方法返回迭代器對(duì)象本身坠宴;next()方法返回容器的下一個(gè)元素,在結(jié)尾時(shí)引發(fā)StopIteration異常绷旗。

__iter__()和next()方法

這兩個(gè)方法是迭代器最基本的方法喜鼓,一個(gè)用來獲得迭代器對(duì)象,一個(gè)用來獲取容器中的下一個(gè)元素衔肢。
對(duì)于可迭代對(duì)象庄岖,可以使用內(nèi)建函數(shù)iter()來獲取它的迭代器對(duì)象
(注意可迭代對(duì)象迭代器對(duì)象的區(qū)別,可迭代對(duì)象比如list等,通過iter(list)獲得它的迭代器對(duì)象角骤,可迭代對(duì)象可以重復(fù)遍歷隅忿,迭代器對(duì)象由于next()方法的存在,不可重復(fù)遍歷)


例子中邦尊,通過iter()方法獲得了list的迭代器對(duì)象背桐,然后就可以通過next()方法來訪問list中的元素了。當(dāng)容器中沒有可訪問的元素后蝉揍,next()方法將會(huì)拋出一個(gè)StopIteration異常終止迭代器链峭。
其實(shí),當(dāng)我們使用for語句的時(shí)候又沾,for語句就會(huì)自動(dòng)的通過__iter__()方法來獲得迭代器對(duì)象弊仪,并且通過next()方法來獲取下一個(gè)元素熙卡。

iter()方法

iter()方法返回迭代器對(duì)象。如果本身是迭代器對(duì)象励饵,則iter()方法直接返回這個(gè)迭代器對(duì)象驳癌。反之,如果傳給iter函數(shù)的不是迭代器對(duì)象役听,則iter()函數(shù)每次都會(huì)返回新的迭代器對(duì)象颓鲜。如:



所以,想判斷某個(gè)值是迭代器還是非迭代器禾嫉,可以用該值為參數(shù)灾杰,兩次調(diào)用iter函數(shù),若結(jié)果相同則是迭代器熙参。

自定義迭代器

了解了迭代器協(xié)議之后艳吠,就可以自定義迭代器了。
下面例子中實(shí)現(xiàn)了一個(gè)MyRange的類型孽椰,這個(gè)類型中實(shí)現(xiàn)了__iter__()方法昭娩,通過這個(gè)方法返回對(duì)象本身作為迭代器對(duì)象;同時(shí)黍匾,實(shí)現(xiàn)了next()方法用來獲取容器中的下一個(gè)元素栏渺,當(dāng)沒有可訪問元素后,就拋出StopIteration異常锐涯。

class MyRange(object):
 def __init__(self, n):
  self.idx = 0
  self.n = n

 def __iter__(self):
  return self

 def next(self):
  if self.idx < self.n:
   val = self.idx
   self.idx += 1
   return val
  else:
   raise StopIteration()

這個(gè)自定義類型跟內(nèi)建函數(shù)xrange很類似磕诊,看一下運(yùn)行結(jié)果:

myRange = MyRange(3)
for i in myRange:
 print i
迭代器對(duì)象和可迭代對(duì)象

在上面的例子中,myRange這個(gè)對(duì)象就是一個(gè)可迭代對(duì)象纹腌,同時(shí)它本身也是一個(gè)迭代器對(duì)象霎终。
看下面的代碼,對(duì)于一個(gè)可迭代對(duì)象升薯,如果它本身又是一個(gè)迭代器對(duì)象(同時(shí)實(shí)現(xiàn)了__iter__和next()方法)莱褒,就會(huì)有下面的 問題,就沒有辦法支持多次迭代涎劈。


為了解決上面的問題广凸,可以分別定義可迭代類型對(duì)象和迭代器類型對(duì)象;然后可迭代類型對(duì)象的__iter__()方法可以獲得一個(gè)迭代器類型的對(duì)象蛛枚×潞#看下面的實(shí)現(xiàn):

class Zrange:
 def __init__(self, n):
  self.n = n

 def __iter__(self):
  return ZrangeIterator(self.n)

class ZrangeIterator:
 def __init__(self, n):
  self.i = 0
  self.n = n

 def __iter__(self):
  return self

 def next(self):
  if self.i < self.n:
   i = self.i
   self.i += 1
   return i
  else:
   raise StopIteration() 

zrange = Zrange(3)
print zrange is iter(zrange)   

print [i for i in zrange]
print [i for i in zrange]

代碼的運(yùn)行結(jié)果為:



其實(shí),通過下面代碼可以看出蹦浦,list類型也是按照上面的方式扭吁,list本身是一個(gè)可迭代對(duì)象,通過iter()方法可以獲得list的迭代器對(duì)象:


生成器

在Python中,使用生成器可以很方便的支持迭代器協(xié)議智末。生成器通過生成器函數(shù)產(chǎn)生,生成器函數(shù)可以通過常規(guī)的def語句來定義徒河,但是不用return返回系馆,而是用yield一次返回一個(gè)結(jié)果,在每個(gè)結(jié)果之間掛起和繼續(xù)它們的狀態(tài)顽照,來自動(dòng)實(shí)現(xiàn)迭代協(xié)議由蘑。
也就是說,yield是一個(gè)語法糖代兵,內(nèi)部實(shí)現(xiàn)支持了迭代器協(xié)議尼酿,同時(shí)yield內(nèi)部是一個(gè)狀態(tài)機(jī),維護(hù)著掛起和繼續(xù)的狀態(tài)植影。
下面看看生成器的使用:


在這個(gè)例子中裳擎,定義了一個(gè)生成器函數(shù),函數(shù)返回一個(gè)生成器對(duì)象思币,然后就可以通過for語句進(jìn)行迭代訪問了鹿响。
其實(shí),生成器函數(shù)返回生成器的迭代器谷饿。 “生成器的迭代器”這個(gè)術(shù)語通常被稱作”生成器”惶我。要注意的是生成器就是一類特殊的迭代器。作為一個(gè)迭代器博投,生成器必須要定義一些方法绸贡,其中一個(gè)就是next()。如同迭代器一樣毅哗,我們可以使用next()函數(shù)來獲取下一個(gè)值听怕。

生成器執(zhí)行流程

下面就仔細(xì)看看生成器是怎么工作的。
從上面的例子也可以看到黎做,生成器函數(shù)跟普通的函數(shù)是有很大差別的叉跛。
結(jié)合上面的例子我們加入一些打印信息,進(jìn)一步看看生成器的執(zhí)行流程:



通過結(jié)果可以看到:
當(dāng)調(diào)用生成器函數(shù)的時(shí)候蒸殿,函數(shù)只是返回了一個(gè)生成器對(duì)象筷厘,并沒有 執(zhí)行。
當(dāng)next()方法第一次被調(diào)用的時(shí)候宏所,生成器函數(shù)才開始執(zhí)行酥艳,執(zhí)行到y(tǒng)ield語句處停止
next()方法的返回值就是yield語句處的參數(shù)(yielded value)
當(dāng)繼續(xù)調(diào)用next()方法的時(shí)候,函數(shù)將接著上一次停止的yield語句處繼續(xù)執(zhí)行爬骤,并到下一個(gè)yield處停止充石;如果后面沒有yield就拋出StopIteration異常。

生成器表達(dá)式

在開始介紹生成器表達(dá)式之前霞玄,先看看我們比較熟悉的列表解析( List comprehensions)骤铃,列表解析一般都是下面的形式拉岁。

[expr for iter_var in iterable if cond_expr]

迭代iterable里所有內(nèi)容,每一次迭代后惰爬,把iterable里滿足cond_expr條件的內(nèi)容放到iter_var中喊暖,再在表達(dá)式expr中應(yīng)該iter_var的內(nèi)容,最后用表達(dá)式的計(jì)算值生成一個(gè)列表撕瞧。

生成器表達(dá)式是在python2.4中引入的陵叽,當(dāng)序列過長(zhǎng), 而每次只需要獲取一個(gè)元素時(shí)丛版,應(yīng)當(dāng)考慮使用生成器表達(dá)式而不是列表解析巩掺。生成器表達(dá)式的語法和列表解析一樣,只不過生成器表達(dá)式是被()括起來的页畦,而不是[]胖替,如下:

(expr for iter_var in iterable if cond_expr)

生成器表達(dá)式并不是創(chuàng)建一個(gè)列表, 而是返回一個(gè)生成器寇漫,這個(gè)生成器在每次計(jì)算出一個(gè)條目后刊殉,把這個(gè)條目”產(chǎn)生”(yield)出來。 生成器表達(dá)式使用了”惰性計(jì)算”(lazy evaluation)州胳,只有在檢索時(shí)才被賦值(evaluated)记焊,所以在列表比較長(zhǎng)的情況下使用內(nèi)存上更有效。
一個(gè)例子:



從這個(gè)例子中可以看到栓撞,生成器表達(dá)式產(chǎn)生的生成器遍膜,它自身是一個(gè)可迭代對(duì)象,同時(shí)也是迭代器本身瓤湘。

總結(jié):

本文介紹了Python迭代器和生成器的相關(guān)內(nèi)容瓢颅。
1. 通過實(shí)現(xiàn)迭代器協(xié)議對(duì)應(yīng)的__iter__()和next()方法,可以自定義迭代器類型弛说。對(duì)于可迭代對(duì)象挽懦,for語句可以通過iter()方法獲取迭代器,并且通過next()方法獲得容器的下一個(gè)元素木人。
2. 像列表這種序列類型的對(duì)象信柿,可迭代對(duì)象和迭代器對(duì)象是相互獨(dú)立存在的,在迭代的過程中各個(gè)迭代器相互獨(dú)立醒第;但是渔嚷,有的可迭代對(duì)象本身又是迭代器對(duì)象,那么迭代器就沒法獨(dú)立使用稠曼。
3. itertools模塊提供了一系列迭代器形病,能夠幫助用戶輕松地使用排列、組合、笛卡爾積或其他組合結(jié)構(gòu)漠吻。
4. 生成器是一種特殊的迭代器量瓜,內(nèi)部支持了生成器協(xié)議,不需要明確定義__iter__()和next()方法途乃。
5. 生成器通過生成器函數(shù)產(chǎn)生榔至,生成器函數(shù)可以通過常規(guī)的def語句來定義,但是不用return返回欺劳,而是用yield一次返回一個(gè)結(jié)果。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铅鲤,一起剝皮案震驚了整個(gè)濱河市划提,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌邢享,老刑警劉巖鹏往,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異骇塘,居然都是意外死亡伊履,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門款违,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唐瀑,“玉大人,你說我怎么就攤上這事插爹『謇保” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵赠尾,是天一觀的道長(zhǎng)力穗。 經(jīng)常有香客問我,道長(zhǎng)气嫁,這世上最難降的妖魔是什么当窗? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮寸宵,結(jié)果婚禮上崖面,老公的妹妹穿的比我還像新娘。我一直安慰自己邓馒,他們只是感情好嘶朱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著光酣,像睡著了一般疏遏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天财异,我揣著相機(jī)與錄音倘零,去河邊找鬼。 笑死戳寸,一個(gè)胖子當(dāng)著我的面吹牛呈驶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疫鹊,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼袖瞻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了拆吆?” 一聲冷哼從身側(cè)響起聋迎,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枣耀,沒想到半個(gè)月后霉晕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捞奕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年牺堰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颅围。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伟葫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出院促,到底是詐尸還是另有隱情扒俯,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布一疯,位于F島的核電站撼玄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏墩邀。R本人自食惡果不足惜掌猛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望眉睹。 院中可真熱鬧荔茬,春花似錦、人聲如沸竹海。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斋配。三九已至孔飒,卻和暖如春灌闺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坏瞄。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工桂对, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸠匀。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓蕉斜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親缀棍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宅此,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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