您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Python中對生成器的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學習一下“Python中對生成器的示例分析”這篇文章吧。
1. 生成器(generator)
1.1. 生成器簡介
首先請確信,生成器就是一種迭代器。生成器擁有next方法并且行為與迭代器完全相同,這意味著生成器也可以用于Python的for循環(huán)中。另外,對于生成器的特殊語法支持使得編寫一個生成器比自定義一個常規(guī)的迭代器要簡單不少,所以生成器也是最常用到的特性之一。
從Python 2.5開始,[PEP 342:通過增強生成器實現(xiàn)協(xié)同程序]的實現(xiàn)為生成器加入了更多的特性,這意味著生成器還可以完成更多的工作。這部分我們會在稍后的部分介紹。
1.2. 生成器函數(shù)
1.2.1. 使用生成器函數(shù)定義生成器
如何獲取一個生成器?首先來看一小段代碼:
>>> def get_0_1_2(): ... yield 0 ... yield 1 ... yield 2 ... >>> get_0_1_2 <function get_0_1_2 at 0x00B2CB70>
我們定義了一個函數(shù)get_0_1_2,并且可以查看到這確實是函數(shù)類型。但與一般的函數(shù)不同的是,get_0_1_2的函數(shù)體內(nèi)使用了關(guān)鍵字yield,這使得get_0_1_2成為了一個生成器函數(shù)。生成器函數(shù)的特性如下:
調(diào)用生成器函數(shù)將返回一個生成器;
>>> generator = get_0_1_2() >>> generator <generator object get_0_1_2 at 0x00B1C7D8>
第一次調(diào)用生成器的next方法時,生成器才開始執(zhí)行生成器函數(shù)(而不是構(gòu)建生成器時),直到遇到y(tǒng)ield時暫停執(zhí)行(掛起),并且yield的參數(shù)將作為此次next方法的返回值;
>>> generator.next() 0
之后每次調(diào)用生成器的next方法,生成器將從上次暫停執(zhí)行的位置恢復執(zhí)行生成器函數(shù),直到再次遇到y(tǒng)ield時暫停,并且同樣的,yield的參數(shù)將作為next方法的返回值;
>>> generator.next() 1 >>> generator.next() 2
如果當調(diào)用next方法時生成器函數(shù)結(jié)束(遇到空的return語句或是到達函數(shù)體末尾),則這次next方法的調(diào)用將拋出StopIteration異常(即for循環(huán)的終止條件);
>>> generator.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
生成器函數(shù)在每次暫停執(zhí)行時,函數(shù)體內(nèi)的所有變量都將被封存(freeze)在生成器中,并將在恢復執(zhí)行時還原,并且類似于閉包,即使是同一個生成器函數(shù)返回的生成器,封存的變量也是互相獨立的。
我們的小例子中并沒有用到變量,所以這里另外定義一個生成器來展示這個特點:
>>> def fibonacci(): ... a = b = 1 ... yield a ... yield b ... while True: ... a, b = b, a+b ... yield b ... >>> for num in fibonacci(): ... if num > 100: break ... print num, ... 1 1 2 3 5 8 13 21 34 55 89
看到while True可別太吃驚,因為生成器可以掛起,所以是延遲計算的,無限循環(huán)并沒有關(guān)系。這個例子中我們定義了一個生成器用于獲取斐波那契數(shù)列。
1.2.2. 生成器函數(shù)的FAQ
接下來我們來討論一些關(guān)于生成器的有意思的話題。
你的例子里生成器函數(shù)都沒有參數(shù),那么生成器函數(shù)可以帶參數(shù)嗎?
當然可以啊親,而且它支持函數(shù)的所有參數(shù)形式。要知道生成器函數(shù)也是函數(shù)的一種:)
>>> def counter(start=0): ... while True: ... yield start ... start += 1 ...
這是一個從指定數(shù)開始的計數(shù)器。
既然生成器函數(shù)也是函數(shù),那么它可以使用return輸出返回值嗎?
不行的親,是這樣的,生成器函數(shù)已經(jīng)有默認的返回值——生成器了,你不能再另外給一個返回值;對,即使是return None也不行。但是它可以使用空的return語句結(jié)束。如果你堅持要為它指定返回值,那么Python將在定義的位置贈送一個語法錯誤異常,就像這樣:
>>> def i_wanna_return(): ... yield None ... return None ... File "<stdin>", line 3 SyntaxError: 'return' with argument inside generator
好吧,那人家需要確保釋放資源,需要在try...finally中yield,這會是神馬情況?(我就是想玩你)我在finally中還yield了一次!
Python會在真正離開try...finally時再執(zhí)行finally中的代碼,而這里遺憾地告訴你,暫停不算哦!所以結(jié)局你也能猜到吧!
>>> def play_u(): ... try: ... yield 1 ... yield 2 ... yield 3 ... finally: ... yield 0 ... >>> for val in play_u(): print val, ... 1 2 3 0
*這與return的情況不同。return是真正的離開代碼塊,所以會在return時立刻執(zhí)行finally子句。
*另外,“在帶有finally子句的try塊中yield”定義在PEP 342中,這意味著只有Python 2.5以上版本才支持這個語法,在Python 2.4以下版本中會得到語法錯誤異常。
如果我需要在生成器的迭代過程中接入另一個生成器的迭代怎么辦?寫成下面這樣好傻好天真。。
>>> def sub_generator(): ... yield 1 ... yield 2 ... for val in counter(10): yield val ...
這種情況的語法改進已經(jīng)被定義在[PEP 380:委托至子生成器的語法]中,據(jù)說會在Python 3.3中實現(xiàn),屆時也可能回饋到2.x中。實現(xiàn)后,就可以這么寫了:
>>> def sub_generator(): ... yield 1 ... yield 2 ... yield from counter(10) File "<stdin>", line 4 yield from counter(10) ^ SyntaxError: invalid syntax
看到語法錯誤木有?現(xiàn)在我們還是天真一點吧~
有更多問題?請回復此文:)
1.3. 協(xié)同程序(coroutine)
協(xié)同程序(協(xié)程)一般來說是指這樣的函數(shù):
彼此間有不同的局部變量、指令指針,但仍共享全局變量;
可以方便地掛起、恢復,并且有多個入口點和出口點;
多個協(xié)同程序間表現(xiàn)為協(xié)作運行,如A的運行過程中需要B的結(jié)果才能繼續(xù)執(zhí)行。
協(xié)程的特點決定了同一時刻只能有一個協(xié)同程序正在運行(忽略多線程的情況)。得益于此,協(xié)程間可以直接傳遞對象而不需要考慮資源鎖、或是直接喚醒其他協(xié)程而不需要主動休眠,就像是內(nèi)置了鎖的線程。在符合協(xié)程特點的應(yīng)用場景,使用協(xié)程無疑比使用線程要更方便。
從另一方面說,協(xié)程無法并發(fā)其實也將它的應(yīng)用場景限制在了一個很狹窄的范圍,這個特點使得協(xié)程更多的被拿來與常規(guī)函數(shù)進行比較,而不是與線程。當然,線程比協(xié)程復雜許多,功能也更強大,所以我建議大家牢牢地掌握線程即可:Python線程指南分享
這一節(jié)里我也就不列舉關(guān)于協(xié)程的例子了,以下介紹的方法了解即可。
Python 2.5對生成器的增強實現(xiàn)了協(xié)程的其他特點,在這個版本中,生成器加入了如下方法:
send(value):
send是除next外另一個恢復生成器的方法。Python 2.5中,yield語句變成了yield表達式,這意味著yield現(xiàn)在可以有一個值,而這個值就是在生成器的send方法被調(diào)用從而恢復執(zhí)行時,調(diào)用send方法的參數(shù)。
>>> def repeater(): ... n = 0 ... while True: ... n = (yield n) ... >>> r = repeater() >>> r.next() 0 >>> r.send(10) 10
*調(diào)用send傳入非None值前,生成器必須處于掛起狀態(tài),否則將拋出異常。不過,未啟動的生成器仍可以使用None作為參數(shù)調(diào)用send。
*如果使用next恢復生成器,yield表達式的值將是None。
close():
這個方法用于關(guān)閉生成器。對關(guān)閉的生成器后再次調(diào)用next或send將拋出StopIteration異常。
throw(type, value=None, traceback=None):
這個方法用于在生成器內(nèi)部(生成器的當前掛起處,或未啟動時在定義處)拋出一個異常。
*別為沒見到協(xié)程的例子遺憾,協(xié)程最常見的用處其實就是生成器。
1.4. 一個有趣的庫:pipe
這一節(jié)里我要向諸位簡要介紹pipe。pipe并不是Python內(nèi)置的庫,如果你安裝了easy_install,直接可以安裝它,否則你需要自己下載它:http://pypi.python.org/pypi/pipe
之所以要介紹這個庫,是因為它向我們展示了一種很有新意的使用迭代器和生成器的方式:流。pipe將可迭代的數(shù)據(jù)看成是流,類似于linux,pipe使用'|'傳遞數(shù)據(jù)流,并且定義了一系列的“流處理”函數(shù)用于接受并處理數(shù)據(jù)流,并最終再次輸出數(shù)據(jù)流或者是將數(shù)據(jù)流歸納得到一個結(jié)果。我們來看一些例子。
第一個,非常簡單的,使用add求和:
>>> from pipe import * >>> range(5) | add 10
求偶數(shù)和需要使用到where,作用類似于內(nèi)建函數(shù)filter,過濾出符合條件的元素:
>>> range(5) | where(lambda x: x % 2 == 0) | add 6
還記得我們定義的斐波那契數(shù)列生成器嗎?求出數(shù)列中所有小于10000的偶數(shù)和需要用到take_while,與itertools的同名函數(shù)有類似的功能,截取元素直到條件不成立:
>>> fib = fibonacci >>> fib() | where(lambda x: x % 2 == 0)\ ... | take_while(lambda x: x < 10000)\ ... | add 3382
需要對元素應(yīng)用某個函數(shù)可以使用select,作用類似于內(nèi)建函數(shù)map;需要得到一個列表,可以使用as_list:
>>> fib() | select(lambda x: x ** 2) | take_while(lambda x: x < 100) | as_list [1, 1, 4, 9, 25, 64]
pipe中還包括了更多的流處理函數(shù)。你甚至可以自己定義流處理函數(shù),只需要定義一個生成器函數(shù)并加上修飾器Pipe。如下定義了一個獲取元素直到索引不符合條件的流處理函數(shù):
>>> @Pipe ... def take_while_idx(iterable, predicate): ... for idx, x in enumerate(iterable): ... if predicate(idx): yield x ... else: return ...
使用這個流處理函數(shù)獲取fib的前10個數(shù)字:
>>> fib() | take_while_idx(lambda x: x < 10) | as_list [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
更多的函數(shù)就不在這里介紹了,你可以查看pipe的源文件,總共600行不到的文件其中有300行是文檔,文檔中包含了大量的示例。
pipe實現(xiàn)起來非常簡單,使用Pipe裝飾器,將普通的生成器函數(shù)(或者返回迭代器的函數(shù))代理在一個實現(xiàn)了__ror__方法的普通類實例上即可,但是這種思路真的很有趣。
函數(shù)式編程指南全文到這里就全部結(jié)束了,希望這一系列文章能給你帶來幫助。希望大家都能看到一些結(jié)構(gòu)式編程之外的編程方式,并且能夠熟練地在恰當?shù)牡胤绞褂?:)
以上是“Python中對生成器的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。