欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2015年06月24日 10:57:27   投稿:junjie  
這篇文章主要介紹了Python函數(shù)式編程指南(四):生成器詳解,本文講解了生成器簡(jiǎn)介、生成器函數(shù)、生成器函數(shù)的FAQ等內(nèi)容,需要的朋友可以參考下

4. 生成器(generator)

4.1. 生成器簡(jiǎn)介

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

從Python 2.5開始,[PEP 342:通過(guò)增強(qiáng)生成器實(shí)現(xiàn)協(xié)同程序]的實(shí)現(xiàn)為生成器加入了更多的特性,這意味著生成器還可以完成更多的工作。這部分我們會(huì)在稍后的部分介紹。

4.2. 生成器函數(shù)

4.2.1. 使用生成器函數(shù)定義生成器

如何獲取一個(gè)生成器?首先來(lái)看一小段代碼:

復(fù)制代碼 代碼如下:

>>> def get_0_1_2():
...   yield 0
...   yield 1
...   yield 2
...
>>> get_0_1_2
<function get_0_1_2 at 0x00B2CB70>

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

1.調(diào)用生成器函數(shù)將返回一個(gè)生成器;

復(fù)制代碼 代碼如下:

>>> generator = get_0_1_2()
>>> generator
<generator object get_0_1_2 at 0x00B1C7D8>

2.第一次調(diào)用生成器的next方法時(shí),生成器才開始執(zhí)行生成器函數(shù)(而不是構(gòu)建生成器時(shí)),直到遇到y(tǒng)ield時(shí)暫停執(zhí)行(掛起),并且yield的參數(shù)將作為此次next方法的返回值;
復(fù)制代碼 代碼如下:

>>> generator.next()
0

3.之后每次調(diào)用生成器的next方法,生成器將從上次暫停執(zhí)行的位置恢復(fù)執(zhí)行生成器函數(shù),直到再次遇到y(tǒng)ield時(shí)暫停,并且同樣的,yield的參數(shù)將作為next方法的返回值;
復(fù)制代碼 代碼如下:

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

4.如果當(dāng)調(diào)用next方法時(shí)生成器函數(shù)結(jié)束(遇到空的return語(yǔ)句或是到達(dá)函數(shù)體末尾),則這次next方法的調(diào)用將拋出StopIteration異常(即for循環(huán)的終止條件);
復(fù)制代碼 代碼如下:

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

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

復(fù)制代碼 代碼如下:

>>> 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可別太吃驚,因?yàn)樯善骺梢話炱穑允茄舆t計(jì)算的,無(wú)限循環(huán)并沒(méi)有關(guān)系。這個(gè)例子中我們定義了一個(gè)生成器用于獲取斐波那契數(shù)列。

4.2.2. 生成器函數(shù)的FAQ
接下來(lái)我們來(lái)討論一些關(guān)于生成器的有意思的話題。

1.你的例子里生成器函數(shù)都沒(méi)有參數(shù),那么生成器函數(shù)可以帶參數(shù)嗎?

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

復(fù)制代碼 代碼如下:

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

這是一個(gè)從指定數(shù)開始的計(jì)數(shù)器。

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

不行的親,是這樣的,生成器函數(shù)已經(jīng)有默認(rèn)的返回值——生成器了,你不能再另外給一個(gè)返回值;對(duì),即使是return None也不行。但是它可以使用空的return語(yǔ)句結(jié)束。如果你堅(jiān)持要為它指定返回值,那么Python將在定義的位置贈(zèng)送一個(gè)語(yǔ)法錯(cuò)誤異常,就像這樣:

復(fù)制代碼 代碼如下:

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

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

復(fù)制代碼 代碼如下:

>>> 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是真正的離開代碼塊,所以會(huì)在return時(shí)立刻執(zhí)行finally子句。
*另外,“在帶有finally子句的try塊中yield”定義在PEP 342中,這意味著只有Python 2.5以上版本才支持這個(gè)語(yǔ)法,在Python 2.4以下版本中會(huì)得到語(yǔ)法錯(cuò)誤異常。

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

復(fù)制代碼 代碼如下:

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

這種情況的語(yǔ)法改進(jìn)已經(jīng)被定義在[PEP 380:委托至子生成器的語(yǔ)法]中,據(jù)說(shuō)會(huì)在Python 3.3中實(shí)現(xiàn),屆時(shí)也可能回饋到2.x中。實(shí)現(xiàn)后,就可以這么寫了:

復(fù)制代碼 代碼如下:

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

看到語(yǔ)法錯(cuò)誤木有?現(xiàn)在我們還是天真一點(diǎn)吧~

有更多問(wèn)題?請(qǐng)回復(fù)此文:)

4.3. 協(xié)同程序(coroutine)

協(xié)同程序(協(xié)程)一般來(lái)說(shuō)是指這樣的函數(shù):

1.彼此間有不同的局部變量、指令指針,但仍共享全局變量;
2.可以方便地掛起、恢復(fù),并且有多個(gè)入口點(diǎn)和出口點(diǎn);
3.多個(gè)協(xié)同程序間表現(xiàn)為協(xié)作運(yùn)行,如A的運(yùn)行過(guò)程中需要B的結(jié)果才能繼續(xù)執(zhí)行。

協(xié)程的特點(diǎn)決定了同一時(shí)刻只能有一個(gè)協(xié)同程序正在運(yùn)行(忽略多線程的情況)。得益于此,協(xié)程間可以直接傳遞對(duì)象而不需要考慮資源鎖、或是直接喚醒其他協(xié)程而不需要主動(dòng)休眠,就像是內(nèi)置了鎖的線程。在符合協(xié)程特點(diǎn)的應(yīng)用場(chǎng)景,使用協(xié)程無(wú)疑比使用線程要更方便。

從另一方面說(shuō),協(xié)程無(wú)法并發(fā)其實(shí)也將它的應(yīng)用場(chǎng)景限制在了一個(gè)很狹窄的范圍,這個(gè)特點(diǎn)使得協(xié)程更多的被拿來(lái)與常規(guī)函數(shù)進(jìn)行比較,而不是與線程。當(dāng)然,線程比協(xié)程復(fù)雜許多,功能也更強(qiáng)大,所以我建議大家牢牢地掌握線程即可:Python線程指南

這一節(jié)里我也就不列舉關(guān)于協(xié)程的例子了,以下介紹的方法了解即可。

Python 2.5對(duì)生成器的增強(qiáng)實(shí)現(xiàn)了協(xié)程的其他特點(diǎn),在這個(gè)版本中,生成器加入了如下方法:

1.send(value):

send是除next外另一個(gè)恢復(fù)生成器的方法。Python 2.5中,yield語(yǔ)句變成了yield表達(dá)式,這意味著yield現(xiàn)在可以有一個(gè)值,而這個(gè)值就是在生成器的send方法被調(diào)用從而恢復(fù)執(zhí)行時(shí),調(diào)用send方法的參數(shù)。

復(fù)制代碼 代碼如下:

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

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

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

之所以要介紹這個(gè)庫(kù),是因?yàn)樗蛭覀冋故玖艘环N很有新意的使用迭代器和生成器的方式:流。pipe將可迭代的數(shù)據(jù)看成是流,類似于linux,pipe使用'|'傳遞數(shù)據(jù)流,并且定義了一系列的“流處理”函數(shù)用于接受并處理數(shù)據(jù)流,并最終再次輸出數(shù)據(jù)流或者是將數(shù)據(jù)流歸納得到一個(gè)結(jié)果。我們來(lái)看一些例子。

第一個(gè),非常簡(jiǎn)單的,使用add求和:

復(fù)制代碼 代碼如下:

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

求偶數(shù)和需要使用到where,作用類似于內(nèi)建函數(shù)filter,過(guò)濾出符合條件的元素:
復(fù)制代碼 代碼如下:

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

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

復(fù)制代碼 代碼如下:

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

需要對(duì)元素應(yīng)用某個(gè)函數(shù)可以使用select,作用類似于內(nèi)建函數(shù)map;需要得到一個(gè)列表,可以使用as_list:

復(fù)制代碼 代碼如下:

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

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

復(fù)制代碼 代碼如下:

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

使用這個(gè)流處理函數(shù)獲取fib的前10個(gè)數(shù)字:

復(fù)制代碼 代碼如下:

>>> 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實(shí)現(xiàn)起來(lái)非常簡(jiǎn)單,使用Pipe裝飾器,將普通的生成器函數(shù)(或者返回迭代器的函數(shù))代理在一個(gè)實(shí)現(xiàn)了__ror__方法的普通類實(shí)例上即可,但是這種思路真的很有趣。

函數(shù)式編程指南全文到這里就全部結(jié)束了,希望這一系列文章能給你帶來(lái)幫助。希望大家都能看到一些結(jié)構(gòu)式編程之外的編程方式,并且能夠熟練地在恰當(dāng)?shù)牡胤绞褂?:)

明天我會(huì)整理一個(gè)目錄放上來(lái)方便查看,并且列出一些供參考的文章。遺憾的是這些文章幾乎都是英文的,請(qǐng)努力學(xué)習(xí)英語(yǔ)吧 - -#

相關(guān)文章

  • Python掃描IP段查看指定端口是否開放的方法

    Python掃描IP段查看指定端口是否開放的方法

    這篇文章主要介紹了Python掃描IP段查看指定端口是否開放的方法,涉及Python使用socket模塊實(shí)現(xiàn)端口掃描功能的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • 九個(gè)Python列表生成式高頻面試題匯總

    九個(gè)Python列表生成式高頻面試題匯總

    本文為大家整理了九個(gè)Python列表生成式的面試題(從簡(jiǎn)單到困難排序),可以幫助大家提高列表生成式的理解水平,感興趣的小伙伴可以學(xué)習(xí)一下
    2022-05-05
  • Python使用5行代碼批量做小姐姐的素描圖

    Python使用5行代碼批量做小姐姐的素描圖

    本文主要介紹了Python使用5行代碼批量做小姐姐的素描圖,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • scrapy-redis的安裝部署步驟講解

    scrapy-redis的安裝部署步驟講解

    今天小編就為大家分享一篇關(guān)于scrapy-redis的安裝部署步驟講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • Python3 JSON 數(shù)據(jù)解析及日期和時(shí)間小結(jié)

    Python3 JSON 數(shù)據(jù)解析及日期和時(shí)間小結(jié)

    這篇文章主要介紹了Python3 JSON 數(shù)據(jù)解析及日期和時(shí)間,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • python實(shí)現(xiàn)簡(jiǎn)單銀行管理系統(tǒng)

    python實(shí)現(xiàn)簡(jiǎn)單銀行管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)簡(jiǎn)單銀行管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • 詳解Django-restframework 之頻率源碼分析

    詳解Django-restframework 之頻率源碼分析

    這篇文章主要介紹了Django-restframework 之頻率源碼分析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-02-02
  • Python單例模式實(shí)例詳解

    Python單例模式實(shí)例詳解

    這篇文章主要介紹了Python單例模式,結(jié)合實(shí)例形式分析了單例模式的概念、實(shí)現(xiàn)與使用方法、已經(jīng)相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-03-03
  • Python安裝Graphviz?超詳細(xì)圖文教程

    Python安裝Graphviz?超詳細(xì)圖文教程

    這篇文章主要介紹了Python安裝Graphviz?詳細(xì)教程,在Python安裝Graphviz畫圖器,首先要明確他是一個(gè)獨(dú)立的軟件,如果大家用pip的方法裝了graphviz可以先卸載,本文通過(guò)圖文并茂的形式詳細(xì)講解,需要的朋友參考下吧
    2023-02-02
  • 用Python在Excel里畫出蒙娜麗莎的方法示例

    用Python在Excel里畫出蒙娜麗莎的方法示例

    這篇文章主要介紹了用Python在Excel里畫出蒙娜麗莎的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04

最新評(píng)論