迭代器
迭代器協(xié)議:
對(duì)象需要提供next()方法,它要么返回迭代中的下一項(xiàng),要么就引起一個(gè)StopIteration異常,以終止迭代。
可迭代對(duì)象:
實(shí)現(xiàn)了迭代器協(xié)議對(duì)象。list、tuple、dict都是Iterable(可迭代對(duì)象),但不是iteration(迭代器對(duì)象)。但可以使用內(nèi)建函數(shù)iter(),把這些都變成iteration(迭代器對(duì)象)。
為什么在python中,文件還可以使用for循環(huán)進(jìn)行遍歷呢?這是因?yàn)椋趐ython中,文件對(duì)象實(shí)現(xiàn)了迭代器協(xié)議,for循環(huán)并不知道它遍歷的是一個(gè)文件對(duì)象,它只管使用迭代器協(xié)議訪問(wèn)對(duì)象即可。正是由于python的文件對(duì)象實(shí)現(xiàn)了迭代器協(xié)議,我們才得以使用如此方便的方式訪問(wèn)文件,如下所示:
>>>hasattr(open(__file__),'__iter__')
True
為什么list、dict、str等數(shù)據(jù)類型不是Iterator?
1.Python的Iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流,雖然這個(gè)數(shù)據(jù)流看做是一個(gè)有序序列,但是元素的獲得只能通過(guò)next(),因?yàn)镮terator的計(jì)算是惰性的,只有在需要返回下一個(gè)數(shù)據(jù)時(shí)它才會(huì)計(jì)算。
2.而list、dict、str等數(shù)據(jù)類型,提供了對(duì)元素更加方便的操作,可以直接獲取元素,提前知道序列的長(zhǎng)度。
3.但是,iterator也有好處,它可以表示一個(gè)無(wú)限大的數(shù)據(jù)流,例如全體自然數(shù)。而使用list是永遠(yuǎn)不可能存儲(chǔ)全體自然數(shù)的。
生成器
1.兩種產(chǎn)生方式:
①生成器函數(shù):
常規(guī)函數(shù)定義,但是,使用yield語(yǔ)句而不是return語(yǔ)句返回結(jié)果。yield語(yǔ)句一次返回一個(gè)結(jié)果,在每個(gè)結(jié)果中間,掛起函數(shù)的狀態(tài),以便下次從它離開(kāi)的地方繼續(xù)執(zhí)行
defhello():
? ? ?for i in ?range(3):
? ? ? ? ? yield i*i
for i ?in ?hello():
? ? print ?i
注意:在函數(shù)hello被調(diào)用的時(shí)候,返回的是生成器;只有在第一次next()的時(shí)候才會(huì)從頭執(zhí)行函數(shù)直到碰到y(tǒng)ield,之后繼續(xù)從上次yield這一句之后繼續(xù)執(zhí)行,直到不滿足條件,沒(méi)有值可以獲得。
②生成器表達(dá)式:
類似于列表推導(dǎo),只不過(guò)是把一對(duì)大括號(hào)[]變換為一對(duì)小括號(hào)()。但是,生成器表達(dá)式是按需產(chǎn)生一個(gè)生成器結(jié)果對(duì)象,要想拿到每一個(gè)元素,就需要循環(huán)遍歷。
>>>gen = (i*i for i in range(3))
2.好處
①延遲操作。也就是在需要的時(shí)候才產(chǎn)生結(jié)果,不是立即產(chǎn)生結(jié)果。
例如:
print sum( [ i for i in range(10000000000) ?])
print sum( i ? for i in range(10000000000) ? )
第一種方式很容易出現(xiàn)電腦卡死,因?yàn)闀?huì)一次性將list中的內(nèi)容加載到內(nèi)存中
②簡(jiǎn)化代碼,提高代碼可讀性
def ?hello(text):
? ? ? ?result = []
? ? ? ?if ? ?text:
? ? ? ? ? ? ? result.append(0)
? ? ? ?for ?index,letter in enumerate(text,1):
? ? ? ? ? ? ? if ?letter ==' ':
? ? ? ? ? ? ? ? ? ? result.append(index)
? ? ? ?return ? result
def ?hello(text):
? ? ? ? if ? ?text:
? ? ? ? ? ? ? ?yield0
? ? ? ? for ?index,letter ? ?in ?enumerate(text,1):
? ? ? ? ? ? ? if ? ?letter ==' ':
? ? ? ? ? ? ? ? ? ? yield index
注意:不使用生成器的時(shí)候,對(duì)于每次結(jié)果,我們首先看到的是result.append(index),其次,才是index。也就是說(shuō),我們每次看到的是一個(gè)列表的append操作,只是append的是我們想要的結(jié)果。使用生成器的時(shí)候,直接yield index,少了列表append操作的干擾,我們一眼就能夠看出,代碼是要返回index。
3.注意事項(xiàng)
生成器只能遍歷一次,第二次遍歷的時(shí)候返回空
4.深入理解
①與函數(shù)比較:生成器函數(shù)和常規(guī)函數(shù)幾乎是一樣的。它們都是使用def語(yǔ)句進(jìn)行定義,差別在于,生成器使用yield語(yǔ)句返回一個(gè)值,而常規(guī)函數(shù)使用return語(yǔ)句返回一個(gè)值。
②與迭代器關(guān)系:會(huì)自動(dòng)實(shí)現(xiàn)迭代器協(xié)議,以便應(yīng)用到迭代背景中(如for循環(huán),sum函數(shù))。由于生成器自動(dòng)實(shí)現(xiàn)了迭代器協(xié)議,所以,我們可以調(diào)用它的next方法,并且,在沒(méi)有值可以返回的時(shí)候,生成器自動(dòng)產(chǎn)生StopIteration異常。生成器是一種迭代器
③掛起狀態(tài):生成器使用yield語(yǔ)句返回一個(gè)值。yield語(yǔ)句掛起該生成器函數(shù)的狀態(tài),保留足夠的信息,以便之后從它離開(kāi)的地方繼續(xù)執(zhí)行
5.應(yīng)用場(chǎng)景:
1.你不需要重復(fù)讀這些值
2.你可能有海量的子節(jié)點(diǎn),但是不希望將所有節(jié)點(diǎn)放入內(nèi)存