亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python中的迭代器與生成器高級(jí)用法解析

 更新時(shí)間:2016年06月28日 18:40:34   作者:weakish  
這篇文章主要介紹了Python中的迭代器與生成器高級(jí)用法解析,生成器在Python中是迭代器的一種,這里我們會(huì)講到生成表達(dá)式、鏈?zhǔn)缴善鞯壬顚哟蝺?nèi)容,需要的朋友可以參考下

迭代器

迭代器是依附于迭代協(xié)議的對(duì)象——基本意味它有一個(gè)next方法(method),當(dāng)調(diào)用時(shí),返回序列中的下一個(gè)項(xiàng)目。當(dāng)無(wú)項(xiàng)目可返回時(shí),引發(fā)(raise)StopIteration異常。

迭代對(duì)象允許一次循環(huán)。它保留單次迭代的狀態(tài)(位置),或從另一個(gè)角度講,每次循環(huán)序列都需要一個(gè)迭代對(duì)象。這意味我們可以同時(shí)迭代同一個(gè)序列不只一次。將迭代邏輯和序列分離使我們有更多的迭代方式。

調(diào)用一個(gè)容器(container)的__iter__方法創(chuàng)建迭代對(duì)象是掌握迭代器最直接的方式。iter函數(shù)為我們節(jié)約一些按鍵。

>>> nums = [1,2,3]   # note that ... varies: these are different objects
>>> iter(nums)              
<listiterator object at ...>
>>> nums.__iter__()           
<listiterator object at ...>
>>> nums.__reversed__()         
<listreverseiterator object at ...>

>>> it = iter(nums)
>>> next(it)      # next(obj) simply calls obj.next()
1
>>> it.next()
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

當(dāng)在循環(huán)中使用時(shí),StopIteration被接受并停止循環(huán)。但通過(guò)顯式引發(fā)(invocation),我們看到一旦迭代器元素被耗盡,存取它將引發(fā)異常。

使用for...in循環(huán)也使用__iter__方法。這允許我們透明地開(kāi)始對(duì)一個(gè)序列迭代。但是如果我們已經(jīng)有一個(gè)迭代器,我們想在for循環(huán)中能同樣地使用它們。為了實(shí)現(xiàn)這點(diǎn),迭代器除了next還有一個(gè)方法__iter__來(lái)返回迭代器自身(self)。

Python中對(duì)迭代器的支持無(wú)處不在:標(biāo)準(zhǔn)庫(kù)中的所有序列和無(wú)序容器都支持。這個(gè)概念也被拓展到其它東西:例如file對(duì)象支持行的迭代。

>>> f = open('/etc/fstab')
>>> f is f.__iter__()
True

file自身就是迭代器,它的__iter__方法并不創(chuàng)建一個(gè)單獨(dú)的對(duì)象:僅僅單線程的順序讀取被允許。

生成表達(dá)式
第二種創(chuàng)建迭代對(duì)象的方式是通過(guò) 生成表達(dá)式(generator expression) ,列表推導(dǎo)(list comprehension)的基礎(chǔ)。為了增加清晰度,生成表達(dá)式總是封裝在括號(hào)或表達(dá)式中。如果使用圓括號(hào),則創(chuàng)建了一個(gè)生成迭代器(generator iterator)。如果是方括號(hào),這一過(guò)程被‘短路'我們獲得一個(gè)列表list。

>>> (i for i in nums)          
<generator object <genexpr> at 0x...>
>>> [i for i in nums]
[1, 2, 3]
>>> list(i for i in nums)
[1, 2, 3]

在Python 2.7和 3.x中列表表達(dá)式語(yǔ)法被擴(kuò)展到 字典和集合表達(dá)式。一個(gè)集合set當(dāng)生成表達(dá)式是被大括號(hào)封裝時(shí)被創(chuàng)建。一個(gè)字典dict在表達(dá)式包含key:value形式的鍵值對(duì)時(shí)被創(chuàng)建:

>>> {i for i in range(3)}  
set([0, 1, 2])
>>> {i:i**2 for i in range(3)}  
{0: 0, 1: 1, 2: 4}

如果您不幸身陷古老的Python版本中,這個(gè)語(yǔ)法有點(diǎn)糟:

>>> set(i for i in 'abc')
set(['a', 'c', 'b'])
>>> dict((i, ord(i)) for i in 'abc')
{'a': 97, 'c': 99, 'b': 98}

生成表達(dá)式相當(dāng)簡(jiǎn)單,不用多說(shuō)。只有一個(gè)陷阱值得提及:在版本小于3的Python中索引變量(i)會(huì)泄漏。

生成器

生成器是產(chǎn)生一列結(jié)果而不是單一值的函數(shù)。

第三種創(chuàng)建迭代對(duì)象的方式是調(diào)用生成器函數(shù)。一個(gè) 生成器(generator) 是包含關(guān)鍵字yield的函數(shù)。值得注意,僅僅是這個(gè)關(guān)鍵字的出現(xiàn)完全改變了函數(shù)的本質(zhì):yield語(yǔ)句不必引發(fā)(invoke),甚至不必可接觸。但讓函數(shù)變成了生成器。當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),其中的指令被執(zhí)行。而當(dāng)一個(gè)生成器被調(diào)用時(shí),執(zhí)行在其中第一條指令之前停止。生成器的調(diào)用創(chuàng)建依附于迭代協(xié)議的生成器對(duì)象。就像常規(guī)函數(shù)一樣,允許并發(fā)和遞歸調(diào)用。
當(dāng)next被調(diào)用時(shí),函數(shù)執(zhí)行到第一個(gè)yield。每次遇到y(tǒng)ield語(yǔ)句獲得一個(gè)作為next返回的值,在yield語(yǔ)句執(zhí)行后,函數(shù)的執(zhí)行又被停止。

>>> def f():
...  yield 1
...  yield 2
>>> f()                  
<generator object f at 0x...>
>>> gen = f()
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

讓我們遍歷單個(gè)生成器函數(shù)調(diào)用的整個(gè)歷程。

>>> def f():
...  print("-- start --")
...  yield 3
...  print("-- middle --")
...  yield 4
...  print("-- finished --")
>>> gen = f()
>>> next(gen)
-- start --
3
>>> next(gen)
-- middle --
4
>>> next(gen)              
-- finished --
Traceback (most recent call last):
 ...
StopIteration

相比常規(guī)函數(shù)中執(zhí)行f()立即讓print執(zhí)行,gen不執(zhí)行任何函數(shù)體中語(yǔ)句就被賦值。只有當(dāng)gen.next()被next調(diào)用,直到第一個(gè)yield部分的語(yǔ)句才被執(zhí)行。第二個(gè)語(yǔ)句打印-- middle --并在遇到第二個(gè)yield時(shí)停止執(zhí)行。第三個(gè)next打印-- finished --并且到函數(shù)末尾,因?yàn)闆](méi)有yield,引發(fā)了異常。

當(dāng)函數(shù)yield之后控制返回給調(diào)用者后發(fā)生了什么?每個(gè)生成器的狀態(tài)被存儲(chǔ)在生成器對(duì)象中。從這點(diǎn)看生成器函數(shù),好像它是運(yùn)行在單獨(dú)的線程,但這僅僅是假象:執(zhí)行是嚴(yán)格單線程的,但解釋器保留和存儲(chǔ)在下一個(gè)值請(qǐng)求之間的狀態(tài)。

為何生成器有用?正如關(guān)于迭代器這部分強(qiáng)調(diào)的,生成器函數(shù)只是創(chuàng)建迭代對(duì)象的又一種方式。一切能被yield語(yǔ)句完成的東西也能被next方法完成。然而,使用函數(shù)讓解釋器魔力般地創(chuàng)建迭代器有優(yōu)勢(shì)。一個(gè)函數(shù)可以比需要next和__iter__方法的類定義短很多。更重要的是,相比不得不對(duì)迭代對(duì)象在連續(xù)next調(diào)用之間傳遞的實(shí)例(instance)屬性來(lái)說(shuō),生成器的作者能更簡(jiǎn)單的理解局限在局部變量中的語(yǔ)句。

還有問(wèn)題是為何迭代器有用?當(dāng)一個(gè)迭代器用來(lái)驅(qū)動(dòng)循環(huán),循環(huán)變得簡(jiǎn)單。迭代器代碼初始化狀態(tài),決定是否循環(huán)結(jié)束,并且找到下一個(gè)被提取到不同地方的值。這凸顯了循環(huán)體——最值得關(guān)注的部分。除此之外,可以在其它地方重用迭代器代碼。

雙向通信
每個(gè)yield語(yǔ)句將一個(gè)值傳遞給調(diào)用者。這就是為何PEP 255引入生成器(在Python2.2中實(shí)現(xiàn))。但是相反方向的通信也很有用。一個(gè)明顯的方式是一些外部(extern)語(yǔ)句,或者全局變量或共享可變對(duì)象。通過(guò)將先前無(wú)聊的yield語(yǔ)句變成表達(dá)式,直接通信因PEP 342成為現(xiàn)實(shí)(在2.5中實(shí)現(xiàn))。當(dāng)生成器在yield語(yǔ)句之后恢復(fù)執(zhí)行時(shí),調(diào)用者可以對(duì)生成器對(duì)象調(diào)用一個(gè)方法,或者傳遞一個(gè)值 給 生成器,然后通過(guò)yield語(yǔ)句返回,或者通過(guò)一個(gè)不同的方法向生成器注入異常。

第一個(gè)新方法是send(value),類似于next(),但是將value傳遞進(jìn)作為yield表達(dá)式值的生成器中。事實(shí)上,g.next()和g.send(None)是等效的。

第二個(gè)新方法是throw(type, value=None, traceback=None),等效于在yield語(yǔ)句處

raise type, value, traceback

不像raise(從執(zhí)行點(diǎn)立即引發(fā)異常),throw()首先恢復(fù)生成器,然后僅僅引發(fā)異常。選用單次throw就是因?yàn)樗馕吨旬惓7诺狡渌恢?,并且在其它語(yǔ)言中與異常有關(guān)。

當(dāng)生成器中的異常被引發(fā)時(shí)發(fā)生什么?它可以或者顯式引發(fā),當(dāng)執(zhí)行某些語(yǔ)句時(shí)可以通過(guò)throw()方法注入到y(tǒng)ield語(yǔ)句中。任一情況中,異常都以標(biāo)準(zhǔn)方式傳播:它可以被except和finally捕獲,或者造成生成器的中止并傳遞給調(diào)用者。

因完整性緣故,值得提及生成器迭代器也有close()方法,該方法被用來(lái)讓本可以提供更多值的生成器立即中止。它用生成器的__del__方法銷毀保留生成器狀態(tài)的對(duì)象。

讓我們定義一個(gè)只打印出通過(guò)send和throw方法所傳遞東西的生成器。

>>> import itertools
>>> def g():
...   print '--start--'
...   for i in itertools.count():
...     print '--yielding %i--' % i
...     try:
...       ans = yield i
...     except GeneratorExit:
...       print '--closing--'
...       raise
...     except Exception as e:
...       print '--yield raised %r--' % e
...     else:
...       print '--yield returned %s--' % ans

>>> it = g()
>>> next(it)
--start--
--yielding 0--
0
>>> it.send(11)
--yield returned 11--
--yielding 1--
1
>>> it.throw(IndexError)
--yield raised IndexError()--
--yielding 2--
2
>>> it.close()
--closing--

注意: next還是__next__?

在Python 2.x中,接受下一個(gè)值的迭代器方法是next,它通過(guò)全局函數(shù)next顯式調(diào)用,意即它應(yīng)該調(diào)用__next__。就像全局函數(shù)iter調(diào)用__iter__。這種不一致在Python 3.x中被修復(fù),it.next變成了it.__next__。對(duì)于其它生成器方法——send和throw情況更加復(fù)雜,因?yàn)樗鼈儾槐唤忉屍麟[式調(diào)用。然而,有建議語(yǔ)法擴(kuò)展讓continue帶一個(gè)將被傳遞給循環(huán)迭代器中send的參數(shù)。如果這個(gè)擴(kuò)展被接受,可能gen.send會(huì)變成gen.__send__。最后一個(gè)生成器方法close顯然被不正確的命名了,因?yàn)樗呀?jīng)被隱式調(diào)用。

鏈?zhǔn)缴善?br /> 注意: 這是PEP 380的預(yù)覽(還未被實(shí)現(xiàn),但已經(jīng)被Python3.3接受)

比如說(shuō)我們正寫(xiě)一個(gè)生成器,我們想要yield一個(gè)第二個(gè)生成器——一個(gè)子生成器(subgenerator)——生成的數(shù)。如果僅考慮產(chǎn)生(yield)的值,通過(guò)循環(huán)可以不費(fèi)力的完成:

subgen = some_other_generator()
for v in subgen:
  yield v

然而,如果子生成器需要調(diào)用send()、throw()和close()和調(diào)用者適當(dāng)交互的情況下,事情就復(fù)雜了。yield語(yǔ)句不得不通過(guò)類似于前一章節(jié)部分定義的try...except...finally結(jié)構(gòu)來(lái)保證“調(diào)試”生成器函數(shù)。這種代碼在PEP 380中提供,現(xiàn)在足夠拿出將在Python 3.3中引入的新語(yǔ)法了:

yield from some_other_generator()

像上面的顯式循環(huán)調(diào)用一樣,重復(fù)從some_other_generator中產(chǎn)生值直到?jīng)]有值可以產(chǎn)生,但是仍然向子生成器轉(zhuǎn)發(fā)send、throw和close。

相關(guān)文章

  • python向json中追加數(shù)據(jù)的兩種方法總結(jié)

    python向json中追加數(shù)據(jù)的兩種方法總結(jié)

    JSON用來(lái)存儲(chǔ)和交換文本信息,比xml更小/更快/更易解析,下面這篇文章主要給大家介紹了關(guān)于python向json中追加數(shù)據(jù)的兩種方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • Scrapy中詭異xpath的匹配內(nèi)容失效問(wèn)題及解決

    Scrapy中詭異xpath的匹配內(nèi)容失效問(wèn)題及解決

    這篇文章主要介紹了Scrapy中詭異xpath的匹配內(nèi)容失效問(wèn)題及解決方案,具有很好的價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • python中的錯(cuò)誤處理

    python中的錯(cuò)誤處理

    異常是指程序中的例外,違例情況。異常機(jī)制是指程序出現(xiàn)錯(cuò)誤后,程序的處理方法。當(dāng)出現(xiàn)錯(cuò)誤后,程序的執(zhí)行流程發(fā)生改變,程序的控制權(quán)轉(zhuǎn)移到異常處理。
    2016-04-04
  • Python提示[Errno 32]Broken pipe導(dǎo)致線程crash錯(cuò)誤解決方法

    Python提示[Errno 32]Broken pipe導(dǎo)致線程crash錯(cuò)誤解決方法

    這篇文章主要介紹了Python提示[Errno 32]Broken pipe導(dǎo)致線程crash錯(cuò)誤解決方法,是ThreadingHTTPServer實(shí)現(xiàn)http服務(wù)中經(jīng)常會(huì)遇到的問(wèn)題,需要的朋友可以參考下
    2014-11-11
  • python簡(jiǎn)單利用字典破解zip文件口令

    python簡(jiǎn)單利用字典破解zip文件口令

    這篇文章主要給大家介紹了關(guān)于python簡(jiǎn)單利用字典破解zip文件口令的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Pandas之drop_duplicates:去除重復(fù)項(xiàng)方法

    Pandas之drop_duplicates:去除重復(fù)項(xiàng)方法

    下面小編就為大家分享一篇Pandas之drop_duplicates:去除重復(fù)項(xiàng)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • python3使用PyMysql連接mysql數(shù)據(jù)庫(kù)實(shí)例

    python3使用PyMysql連接mysql數(shù)據(jù)庫(kù)實(shí)例

    本篇文章主要介紹了python3使用PyMysql連接mysql數(shù)據(jù)庫(kù)實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02
  • 最新評(píng)論