欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Python函數式編程指南(四):生成器詳解

系統 1887 0

4. 生成器(generator)

4.1. 生成器簡介

首先請確信,生成器就是一種迭代器。生成器擁有next方法并且行為與迭代器完全相同,這意味著生成器也可以用于Python的for循環中。另外,對于生成器的特殊語法支持使得編寫一個生成器比自定義一個常規的迭代器要簡單不少,所以生成器也是最常用到的特性之一。

從Python 2.5開始,[PEP 342:通過增強生成器實現協同程序]的實現為生成器加入了更多的特性,這意味著生成器還可以完成更多的工作。這部分我們會在稍后的部分介紹。

4.2. 生成器函數

4.2.1. 使用生成器函數定義生成器

如何獲取一個生成器?首先來看一小段代碼:

復制代碼 代碼如下:

>>> def get_0_1_2():
...?? yield 0
...?? yield 1
...?? yield 2
...
>>> get_0_1_2

我們定義了一個函數get_0_1_2,并且可以查看到這確實是函數類型。但與一般的函數不同的是,get_0_1_2的函數體內使用了關鍵字yield,這使得get_0_1_2成為了一個生成器函數。生成器函數的特性如下:

1.調用生成器函數將返回一個生成器;

復制代碼 代碼如下:

>>> generator = get_0_1_2()
>>> generator


2.第一次調用生成器的next方法時,生成器才開始執行生成器函數(而不是構建生成器時),直到遇到yield時暫停執行(掛起),并且yield的參數將作為此次next方法的返回值;
復制代碼 代碼如下:

>>> generator.next()
0

3.之后每次調用生成器的next方法,生成器將從上次暫停執行的位置恢復執行生成器函數,直到再次遇到yield時暫停,并且同樣的,yield的參數將作為next方法的返回值;
復制代碼 代碼如下:

>>> generator.next()
1
>>> generator.next()
2

4.如果當調用next方法時生成器函數結束(遇到空的return語句或是到達函數體末尾),則這次next方法的調用將拋出StopIteration異常(即for循環的終止條件);
復制代碼 代碼如下:

>>> generator.next()
Traceback (most recent call last):
? File " ", line 1, in
StopIteration

5.生成器函數在每次暫停執行時,函數體內的所有變量都將被封存(freeze)在生成器中,并將在恢復執行時還原,并且類似于閉包,即使是同一個生成器函數返回的生成器,封存的變量也是互相獨立的。
我們的小例子中并沒有用到變量,所以這里另外定義一個生成器來展示這個特點:

復制代碼 代碼如下:

>>> 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可別太吃驚,因為生成器可以掛起,所以是延遲計算的,無限循環并沒有關系。這個例子中我們定義了一個生成器用于獲取斐波那契數列。

4.2.2. 生成器函數的FAQ
接下來我們來討論一些關于生成器的有意思的話題。

1.你的例子里生成器函數都沒有參數,那么生成器函數可以帶參數嗎?

當然可以啊親,而且它支持函數的所有參數形式。要知道生成器函數也是函數的一種:)

復制代碼 代碼如下:

>>> def counter(start=0):
...?? while True:
...???? yield start
...???? start += 1
...

這是一個從指定數開始的計數器。

2.既然生成器函數也是函數,那么它可以使用return輸出返回值嗎?

不行的親,是這樣的,生成器函數已經有默認的返回值――生成器了,你不能再另外給一個返回值;對,即使是return None也不行。但是它可以使用空的return語句結束。如果你堅持要為它指定返回值,那么Python將在定義的位置贈送一個語法錯誤異常,就像這樣:

復制代碼 代碼如下:

>>> def i_wanna_return():
...?? yield None
...?? return None
...
? File " ", line 3
SyntaxError: 'return' with argument inside generator

3.好吧,那人家需要確保釋放資源,需要在try...finally中yield,這會是神馬情況?(我就是想玩你)我在finally中還yield了一次!
Python會在真正離開try...finally時再執行finally中的代碼,而這里遺憾地告訴你,暫停不算哦!所以結局你也能猜到吧!

復制代碼 代碼如下:

>>> 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時立刻執行finally子句。
*另外,“在帶有finally子句的try塊中yield”定義在PEP 342中,這意味著只有Python 2.5以上版本才支持這個語法,在Python 2.4以下版本中會得到語法錯誤異常。

4.如果我需要在生成器的迭代過程中接入另一個生成器的迭代怎么辦?寫成下面這樣好傻好天真。。

復制代碼 代碼如下:

>>> def sub_generator():
...?? yield 1
...?? yield 2
...?? for val in counter(10): yield val
...

這種情況的語法改進已經被定義在[PEP 380:委托至子生成器的語法]中,據說會在Python 3.3中實現,屆時也可能回饋到2.x中。實現后,就可以這么寫了:

復制代碼 代碼如下:

>>> def sub_generator():
...?? yield 1
...?? yield 2
...?? yield from counter(10)
? File " ", line 4
??? yield from counter(10)
???????????? ^
SyntaxError: invalid syntax

看到語法錯誤木有?現在我們還是天真一點吧~

有更多問題?請回復此文:)

4.3. 協同程序(coroutine)

協同程序(協程)一般來說是指這樣的函數:

1.彼此間有不同的局部變量、指令指針,但仍共享全局變量;
2.可以方便地掛起、恢復,并且有多個入口點和出口點;
3.多個協同程序間表現為協作運行,如A的運行過程中需要B的結果才能繼續執行。

協程的特點決定了同一時刻只能有一個協同程序正在運行(忽略多線程的情況)。得益于此,協程間可以直接傳遞對象而不需要考慮資源鎖、或是直接喚醒其他協程而不需要主動休眠,就像是內置了鎖的線程。在符合協程特點的應用場景,使用協程無疑比使用線程要更方便。

從另一方面說,協程無法并發其實也將它的應用場景限制在了一個很狹窄的范圍,這個特點使得協程更多的被拿來與常規函數進行比較,而不是與線程。當然,線程比協程復雜許多,功能也更強大,所以我建議大家牢牢地掌握線程即可:Python線程指南

這一節里我也就不列舉關于協程的例子了,以下介紹的方法了解即可。

Python 2.5對生成器的增強實現了協程的其他特點,在這個版本中,生成器加入了如下方法:

1.send(value):

send是除next外另一個恢復生成器的方法。Python 2.5中,yield語句變成了yield表達式,這意味著yield現在可以有一個值,而這個值就是在生成器的send方法被調用從而恢復執行時,調用send方法的參數。

復制代碼 代碼如下:

>>> def repeater():
...?? n = 0
...?? while True:
...???? n = (yield n)
...
>>> r = repeater()
>>> r.next()
0
>>> r.send(10)
10

*調用send傳入非None值前,生成器必須處于掛起狀態,否則將拋出異常。不過,未啟動的生成器仍可以使用None作為參數調用send。
*如果使用next恢復生成器,yield表達式的值將是None。
2.close():
這個方法用于關閉生成器。對關閉的生成器后再次調用next或send將拋出StopIteration異常。
3.throw(type, value=None, traceback=None):
這個方法用于在生成器內部(生成器的當前掛起處,或未啟動時在定義處)拋出一個異常。
*別為沒見到協程的例子遺憾,協程最常見的用處其實就是生成器。

4.4. 一個有趣的庫:pipe
這一節里我要向諸位簡要介紹pipe。pipe并不是Python內置的庫,如果你安裝了easy_install,直接可以安裝它,否則你需要自己下載它:http://pypi.python.org/pypi/pipe

之所以要介紹這個庫,是因為它向我們展示了一種很有新意的使用迭代器和生成器的方式:流。pipe將可迭代的數據看成是流,類似于linux,pipe使用'|'傳遞數據流,并且定義了一系列的“流處理”函數用于接受并處理數據流,并最終再次輸出數據流或者是將數據流歸納得到一個結果。我們來看一些例子。

第一個,非常簡單的,使用add求和:

復制代碼 代碼如下:

>>> from pipe import *
>>> range(5) | add
10

求偶數和需要使用到where,作用類似于內建函數filter,過濾出符合條件的元素:
復制代碼 代碼如下:

>>> range(5) | where(lambda x: x % 2 == 0) | add
6

還記得我們定義的斐波那契數列生成器嗎?求出數列中所有小于10000的偶數和需要用到take_while,與itertools的同名函數有類似的功能,截取元素直到條件不成立:

復制代碼 代碼如下:

>>> fib = fibonacci
>>> fib() | where(lambda x: x % 2 == 0)\
...?????? | take_while(lambda x: x < 10000)\
...?????? | add
3382

需要對元素應用某個函數可以使用select,作用類似于內建函數map;需要得到一個列表,可以使用as_list:

復制代碼 代碼如下:

>>> fib() | select(lambda x: x ** 2) | take_while(lambda x: x < 100) | as_list
[1, 1, 4, 9, 25, 64]

pipe中還包括了更多的流處理函數。你甚至可以自己定義流處理函數,只需要定義一個生成器函數并加上修飾器Pipe。如下定義了一個獲取元素直到索引不符合條件的流處理函數:

復制代碼 代碼如下:

>>> @Pipe
... def take_while_idx(iterable, predicate):
...?? for idx, x in enumerate(iterable):
...???? if predicate(idx): yield x
...???? else: return
...

使用這個流處理函數獲取fib的前10個數字:

復制代碼 代碼如下:

>>> fib() | take_while_idx(lambda x: x < 10) | as_list
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

更多的函數就不在這里介紹了,你可以查看pipe的源文件,總共600行不到的文件其中有300行是文檔,文檔中包含了大量的示例。

pipe實現起來非常簡單,使用Pipe裝飾器,將普通的生成器函數(或者返回迭代器的函數)代理在一個實現了__ror__方法的普通類實例上即可,但是這種思路真的很有趣。

函數式編程指南全文到這里就全部結束了,希望這一系列文章能給你帶來幫助。希望大家都能看到一些結構式編程之外的編程方式,并且能夠熟練地在恰當的地方使用 :)

明天我會整理一個目錄放上來方便查看,并且列出一些供參考的文章。遺憾的是這些文章幾乎都是英文的,請努力學習英語吧 - -#


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产成人综合网在线观看 | 青青草免费观看完整版高清 | 欧美一级久久 | 欧美日韩国产一区二区三区不卡 | 中文字幕在线一区 | 亚洲五月婷 | 日韩有码在线观看 | 国产精品久久国产精品 | 欧美成人免费午夜影视 | 高清中文字幕视频在线播 | 欧美日韩在线一区 | 免费大片在线观看网站 | 双性精h调教灌尿打屁股的文案 | 狠狠色狠狠色综合日日92 | 精品小视频在线观看 | 香蕉香蕉国产片一级一级毛片 | 日本在线观看视频网站 | 99热久久这里只有精品首页 | 91视频观看| 在线国产欧美 | 国产专区在线播放 | 男女性刺激爽爽免费视频 | 国产一区 | 成人久久18免费软件 | sese综合 | 九九99九九精彩 | 久久久中文字幕日本 | 欧美一区二区三区在线观看视频 | 日韩中文字幕一区 | a毛片在线看免费观看 | 亚洲第一黄色网 | 亚洲精品人成网在线播放蜜芽 | 一区二区自拍 | 欧美亚洲一区二区三区四区 | 国产欧美精品一区二区三区 | 波多野结衣一区2区3区 | 四虎永久| 亚洲精品黄色 | 日本三级韩国三级香港三级 | 欧美日韩国产在线播放 | 成人午夜精品视频在线观看 |