日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
奇妙的流控制 Python中的迭代器與生成器

在Python 2.2中引進(jìn)了一種帶有新關(guān)鍵字的新型構(gòu)造。這種構(gòu)造是生成器;關(guān)鍵字是yield。生成器使幾個(gè)新型、強(qiáng)大和富有表現(xiàn)力的編程習(xí)慣用法成為可能,但初看,要理解生成器,還是有一點(diǎn)困難。

創(chuàng)新互聯(lián)公司專注于長(zhǎng)海網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供長(zhǎng)海營(yíng)銷型網(wǎng)站建設(shè),長(zhǎng)海網(wǎng)站制作、長(zhǎng)海網(wǎng)頁(yè)設(shè)計(jì)、長(zhǎng)海網(wǎng)站官網(wǎng)定制、重慶小程序開(kāi)發(fā)公司服務(wù),打造長(zhǎng)海網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供長(zhǎng)海網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。

推薦閱讀:深入了解Python暫緩列表生成器

由于迭代器比較容易理解,讓我們先來(lái)看它?;旧希?迭代器是含有 .next() 方法的對(duì)象。唔,這樣定義不十分正確,但非常接近。事實(shí)上,當(dāng)?shù)鲬?yīng)用新的 iter() 內(nèi)置函數(shù)時(shí),大多數(shù)迭代器的上下文希望得到一個(gè)可以生成迭代器的對(duì)象。為使用戶定義的類(該類含有必不可少的 .next() 方法)返回迭代器,需要使 __iter__() 方法返回 self 。本文中的示例會(huì)清楚地說(shuō)明這一點(diǎn)。如果迭代有一個(gè)邏輯終止,則迭代器的 .next() 方法可能決定拋出 StopIteration 異常。

生成器要稍微復(fù)雜和一般化一點(diǎn)。但生成器最典型的用途是用來(lái)定義迭代器;所以不值得總是為一些細(xì)微之處而擔(dān)心。 生成器是這樣一個(gè)函數(shù),它記住上一次返回時(shí)在函數(shù)體中的位置。對(duì)生成器函數(shù)的第二次(或第 n 次)調(diào)用跳轉(zhuǎn)至該函數(shù)中間,而上次調(diào)用的所有局部變量都保持不變。

在某些方面,生成器就象本專欄前面文章討論的函數(shù)型編程中的“終止”。象“終止”一樣,生成器“記住”了它數(shù)據(jù)狀態(tài)。但生成器比“終止”要更進(jìn)一步:生成器還“記住”了它在流控制構(gòu)造(在命令式編程中,這種構(gòu)造不只是數(shù)據(jù)值)中的位置。由于連續(xù)性使您在執(zhí)行框架間任意跳轉(zhuǎn),而不總是返回到直接調(diào)用者的上下文(如同生成器那樣),因此它仍是比較一般的。幸運(yùn)的是,使用生成器比理解程序流和狀態(tài)的所有概念性問(wèn)題容易得多。實(shí)際上,稍加實(shí)踐之后,就可以象普通函數(shù)那樣容易地使用生成器。

隨機(jī)遍歷

讓我們考慮一個(gè)相當(dāng)簡(jiǎn)單的問(wèn)題,可以用多種方法來(lái)解決它 ― 新方法和舊方法都可以。假設(shè)我們想要一串正的隨機(jī)數(shù)字流,它比服從向后參考約束的數(shù)字流要小。明確的講,我們希望每個(gè)后續(xù)數(shù)字比前一個(gè)數(shù)字至少大或小 0.4。而且,數(shù)字流本身不是無(wú)限的,在幾個(gè)隨機(jī)步驟后結(jié)束。這個(gè)示例中,當(dāng)數(shù)字流中產(chǎn)生小于 0.1 的數(shù)字時(shí),我們將簡(jiǎn)單地結(jié)束它。上述的約束有點(diǎn)象可以在“隨機(jī)遍歷”算法找到的約束,結(jié)束條件類似“統(tǒng)計(jì)”或“局部最小值”結(jié)果 ― 但當(dāng)然,這要比大多數(shù)現(xiàn)實(shí)世界中簡(jiǎn)單。在 Python 2.1或更早的版本中,我們有幾種方法來(lái)解決這個(gè)問(wèn)題。一種方法是,簡(jiǎn)單地生成流中的數(shù)字列表并返回它。可能看起來(lái)象:

 
 
 
  1. RandomWalk_List.py  
  2. import  
  3. random  
  4. def  
  5. randomwalk_list  
  6. ():  
  7. last, rand = 1, random.random()   
  8. # init candidate elements  
  9. nums = []     
  10. # empty list  
  11. while  
  12. rand > 0.1:   
  13. # threshhold terminator  
  14. if  
  15. abs(last-rand) >= 0.4:     
  16. # accept the number  
  17.   last = rand 
  18.   nums.append(rand)     
  19. # add latest candidate to nums  
  20. else  
  21. :  
  22. print  
  23. '*',  
  24. # display the rejection  
  25. rand = random.random()    
  26. # new candidate  
  27. nums.append(rand)   
  28. # add the final small element  
  29. return  
  30. nums 

利用這個(gè)函數(shù)就象如下所示般簡(jiǎn)單:

 
 
 
  1. 隨機(jī)遍歷列表的迭代  
  2. for num in randomwalk_list():  
  3. print num, 

上面這種方法中有幾個(gè)值得注意的局限性。這個(gè)特定的示例中極不可能產(chǎn)生龐大的數(shù)字列表,但只通過(guò)將閥值終結(jié)符定義得較嚴(yán)格,就可以創(chuàng)建任意大流(隨機(jī)精確大小,但可以預(yù)見(jiàn)數(shù)量級(jí))。在某種程度上,內(nèi)存和性能問(wèn)題可能使得這種方法不切實(shí)際,以及沒(méi)有必要。同樣是這個(gè)問(wèn)題,使得 Python 較早的版本中添加了 xrange() 和 xreadlines() 。更重要的是,許多流取決于外部事件,并且當(dāng)每個(gè)元素可用時(shí),才處理這些流。例如,流可以偵聽(tīng)一個(gè)端口,或者等待用戶輸入。試圖在流之外創(chuàng)建完整的列表并不就是這些情形中的某一種。

在 Python 2.1 和較早版本中,我們的訣竅是使用“靜態(tài)”函數(shù)局部變量來(lái)記住關(guān)于函數(shù)的上一次調(diào)用的一些事情。顯而易見(jiàn),全局變量可以做同樣的工作,但它們帶來(lái)了大家熟知的全局性名稱空間污染的問(wèn)題,并會(huì)因非局部性而引起錯(cuò)誤。這里,如果您不熟悉這個(gè)訣竅,可能會(huì)感到詫異 ― Python 沒(méi)有“正式”的靜態(tài)范圍聲明。然而,如果賦予了命名參數(shù)可變的缺省值,那么參數(shù)就可以,用作以前調(diào)用的持久存儲(chǔ)器。明確的講,列表是一些便利的可變對(duì)象,他們甚至可以方便地保留多個(gè)值。使用“靜態(tài)”方法,可以編寫(xiě)如下的函數(shù):

 
 
 
  1. RandomWalk_Static.py  
  2. import  
  3. random  
  4. def  
  5. randomwalk_static  
  6. (last=[1]):  
  7. # init the "static" var(s)  
  8. rand = random.random()  
  9. # init a candidate value  
  10. if  
  11. last[0] < 0.1:   
  12. # threshhold terminator  
  13. return  
  14. None     
  15. # end-of-stream flag  
  16. while  
  17. abs(last[0]-rand) < 0.4:    
  18. # look for usable candidate  
  19. print  
  20. '*',  
  21. # display the rejection  
  22. rand = random.random()    
  23. # new candidate  
  24. last[0] = rand  
  25. # update the "static" var  
  26. return  
  27. rand 

這個(gè)函數(shù)是十分友好的存儲(chǔ)器。它只需要記住一個(gè)以前的值,返回一個(gè)單個(gè)數(shù)字(不是一個(gè)數(shù)字的大列表)。并且與此類似的一個(gè)函數(shù)可以返回取決于(部分地或完全地)外部事件的連續(xù)的值。不利的一面是,利用這個(gè)函數(shù)有點(diǎn)不夠簡(jiǎn)練,且相當(dāng)不靈活。

 
 
 
  1. 靜態(tài)隨機(jī)遍歷的迭代  
  2. num = randomwalk_static()  
  3. while num is not None:  
  4. print num,  
  5. num = randomwalk_static() 

#p#

新的遍歷方法

實(shí)質(zhì)上,Python 2.2 序列都是迭代器。Python 常見(jiàn)的習(xí)慣用法 for elem in lst: 現(xiàn)在實(shí)際上讓 lst 產(chǎn)生一個(gè)迭代器。然后, for 循環(huán)反復(fù)調(diào)用這個(gè)迭代器的 .next() 方法,直到它遇到 StopIteration 異常為止。幸運(yùn)的是,由于所有常見(jiàn)的內(nèi)置類型自動(dòng)產(chǎn)生它們的迭代器,所以 Python 程序員不需要知道這里發(fā)生了什么。實(shí)際上,現(xiàn)在字典里有 .iterkeys() 、 .iteritems() 和 .itervalues() 方法來(lái)產(chǎn)生迭代器;首要的是在新的習(xí)慣用法 for key in dct: 中使用了什么。同樣,通過(guò)調(diào)用 .readline() 迭代器支持新的習(xí)慣用法 for line in file: 。

但是如果實(shí)際所產(chǎn)生的是在 Python 解釋器內(nèi),則顯而易見(jiàn)要用定制類來(lái)產(chǎn)生它們自己的迭代器,而不是專使用內(nèi)置類型的迭代器。定制類支持直接使用 randomwalk_list() 以及一次一個(gè)元素這種“極度節(jié)省”的 randomwalk_static ,它是簡(jiǎn)單易懂的:

 
 
 
  1. RandomWalk_Iter.py  
  2. import  
  3. random  
  4. class  
  5. randomwalk_iter  
  6. :  
  7. def  
  8. __init__  
  9. (self):  
  10. self.last = 1   
  11. # init the prior value  
  12. self.rand = random.random()   
  13. # init a candidate value  
  14. def  
  15. __iter__  
  16. (self):  
  17. return  
  18. self     
  19. # simplest iterator creation  
  20. def  
  21. next  
  22. (self):  
  23. if  
  24. self.rand < 0.1:   
  25. # threshhold terminator  
  26. raise  
  27. StopIteration   
  28. # end of iteration  
  29. else  
  30. :   
  31. # look for usable candidate  
  32. while  
  33. abs(self.last-self.rand) < 0.4: 
  34. print  
  35. '*',  
  36. # display the rejection  
  37. self.rand = random.random()   
  38. # new candidate  
  39. selfself.last = self.rand     
  40. # update prior value  
  41. return  
  42. self.rand 

這個(gè)定制迭代器看起來(lái)確實(shí)如同由函數(shù)生成的真實(shí)列表一樣:

 
 
 
  1. 隨機(jī)遍歷類的迭代  
  2. for num in randomwalk_iter():  
  3. print num, 

事實(shí)上,即使支持習(xí)慣用法 if elem in iterator ,它僅嘗試為確定真值所需要的那么多的迭代器的元素,(如果最終的值為 false,當(dāng)然,它就需要測(cè)試所有元素)。

#p#

美中不足

上述方法對(duì)于手邊的問(wèn)題非常好用。但沒(méi)有一種方法能很好地解決這樣的情形:例程在運(yùn)行中創(chuàng)建了大量的局部變量,并把它的運(yùn)行簡(jiǎn)化為循環(huán)和條件的嵌套。如果帶靜態(tài)(或全局)變量的迭代器類或函數(shù)取決于多個(gè)數(shù)據(jù)狀態(tài),則出現(xiàn)兩個(gè)問(wèn)題。一個(gè)是一般性問(wèn)題:創(chuàng)建多個(gè)實(shí)例屬性或靜態(tài)列表元素來(lái)保留每個(gè)數(shù)據(jù)值。更為重要的問(wèn)題是計(jì)算如何確切地返回到與數(shù)據(jù)狀態(tài)相符的流邏輯的相關(guān)部分。非常容易忘記不同數(shù)據(jù)間的相互作用和互相依存。

生成器完全繞過(guò)了整個(gè)問(wèn)題。生成器“返回”時(shí)帶關(guān)鍵字 yield ,但“記住”了它“返回”的所有確切執(zhí)行位置。下次調(diào)用生成器時(shí),它再接著上次的位置 — 包括函數(shù)流和變量值這兩個(gè)方面。

在 Python 2.2+ 中,不直接 寫(xiě)生成器。相反,編寫(xiě)一個(gè)函數(shù),當(dāng)調(diào)用它時(shí),返回生成器。這可能看起來(lái)有點(diǎn)古怪,但“函數(shù)工廠”是 Python 的常見(jiàn)特性,并且“生成器工廠”明顯是這個(gè)概念性擴(kuò)展。在 Python 2.2+ 中使函數(shù)成為生成器工廠是它主體某處的一個(gè)或多個(gè) yield 語(yǔ)句。如果 yield 發(fā)生, return 一定只發(fā)生在沒(méi)有伴隨任何返回值的情況中。然而,一個(gè)較好的選擇是,安排函數(shù)體以便于完成所有 yield 之后,執(zhí)行就“跳轉(zhuǎn)到結(jié)束”。但如果遇到 return ,它導(dǎo)致產(chǎn)生的生成器拋出 StopIteration 異常,而不是進(jìn)一步生成值。

從我的觀點(diǎn)來(lái)看,過(guò)去對(duì)生成器工廠的語(yǔ)法選擇有點(diǎn)欠缺。 yield 語(yǔ)句可以非常好地存在于函數(shù)體中,您可能無(wú)法確定是否函數(shù)一定會(huì)在函數(shù)體最初 N 行內(nèi)的某處作為生成器工廠而存在。當(dāng)然,對(duì)于函數(shù)工廠,也存在這樣的問(wèn)題,但是由于函數(shù)工廠不改變函數(shù)體的實(shí)際 語(yǔ)法(并且有時(shí)允許函數(shù)體返回普通值,盡管這可能不是出自良好的設(shè)計(jì))。對(duì)于我來(lái)說(shuō),新關(guān)鍵字 ― 比如 generator 代替 def ― 會(huì)是一個(gè)比較好的選擇。

先不考慮語(yǔ)法,當(dāng)調(diào)用生成器來(lái)?yè)?dān)當(dāng)?shù)鲿r(shí),生成器有良好的狀況來(lái)自動(dòng)擔(dān)當(dāng)?shù)?。這里不需要象類的 .__iter__() 方法。遇到的每個(gè) yield 都成為生成器的 .next() 方法的返回值。為了清楚起見(jiàn),我們來(lái)看一個(gè)最簡(jiǎn)單的生成器:

 
 
 
  1. 最簡(jiǎn)單可行的 Python 2.2 生成器  
  2. >>>   
  3. from  
  4. __future__   
  5. import  
  6. generators  
  7. >>>   
  8. def  
  9. gen  
  10. ():  
  11. yield 1  
  12. >>> g = gen()  
  13. >>> g.next()  
  14. 1  
  15. >>> g.next()  
  16. Traceback (most recent call last):  
  17. File "", line 1,   
  18. in  
  19. ?  
  20. g.next()  
  21. StopIteration 

讓我們使生成器工作在我們樣本問(wèn)題中:

 
 
 
  1. RandomWalk_Generator.py  
  2. from  
  3. __future__   
  4. import  
  5. generators     
  6. # only needed for Python 2.2  
  7. import  
  8. random  
  9. def  
  10. randomwalk_generator  
  11. ():  
  12. last, rand = 1, random.random()   
  13. # initialize candidate elements  
  14. while  
  15. rand > 0.1:   
  16. # threshhold terminator  
  17. print  
  18. '*',  
  19. # display the rejection  
  20. if  
  21. abs(last-rand) >= 0.4:     
  22. # accept the number  
  23.   last = rand     
  24. # update prior value  
  25.   yield rand  
  26. # return AT THIS POINT  
  27. rand = random.random()    
  28. # new candidate  
  29. yield rand    
  30. # return the final small element 

這個(gè)定義的簡(jiǎn)單性是吸引人的。可以手工或者作為迭代器來(lái)利用這個(gè)生成器。在手工情形下,生成器可以在程序中傳遞,并且無(wú)論在哪里以及無(wú)論何時(shí)需要(這非常靈活),都可以調(diào)用。手工情形的一個(gè)簡(jiǎn)單示例是:

 
 
 
  1. 隨機(jī)遍歷生成器的手工使用  
  2. gen = randomwalk_generator()  
  3. try:  
  4. while 1: print gen.next(),  
  5. except StopIteration:  
  6. pass 

然而,更多情況下,可能將生成器作為迭代器來(lái)使用,這樣更為簡(jiǎn)練(并且看起來(lái)又象只是一個(gè)老式的序列):

 
 
 
  1. 作為迭代器的隨機(jī)遍歷生成器  
  2. for num in randomwalk_generator():  
  3. print_short(num) 

結(jié)束語(yǔ)

Python 程序員需要花一點(diǎn)時(shí)間來(lái)熟悉生成器的來(lái)龍去脈。最初這樣一個(gè)簡(jiǎn)單構(gòu)造所增加的能力是令人驚奇的;并且我預(yù)言,甚至熟練的程序員(象 Python 開(kāi)發(fā)人員自己)也需要花一些時(shí)間來(lái)繼續(xù)發(fā)現(xiàn)使用生成器過(guò)程中的一些微妙的新技術(shù)。

【編輯推薦】

  1. Python閉包的概念、形式與應(yīng)用
  2. 手把手教您Python多線程應(yīng)用技巧
  3. 對(duì)Python特色的詳細(xì)介紹
  4. 加速程序開(kāi)發(fā) Python整合C語(yǔ)言模塊

網(wǎng)頁(yè)名稱:奇妙的流控制 Python中的迭代器與生成器
網(wǎng)站URL:http://www.dlmjj.cn/article/dhepije.html