生成器(第20篇)

假如你需要?jiǎng)?chuàng)建一個(gè)很大很大的列表,但你只需要列表中的某幾個(gè)元素舍悯,這樣就會(huì)造成浪費(fèi)拱层,因?yàn)榇蟛糠值脑貙?duì)你而言都沒(méi)有用骇两,而這些沒(méi)有用的元素卻依舊占用著內(nèi)存。

如果類表中的元素可以通過(guò)某種算法推算出來(lái)拳喻,我們可以在循環(huán)的過(guò)程中不斷推算出來(lái)后續(xù)元素哭当,這樣就不需要?jiǎng)?chuàng)建完整的list了。在Python中就提供了一個(gè)叫生成器的東西可以解決這個(gè)問(wèn)題:generator冗澈。

我們前面學(xué)了列表生成式钦勘,生成器只需要將列表生成式中的[ ]改成( )就可以了。

>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1057af0a0>

如果你想打印 g 中的每一個(gè)元素可以通過(guò)next()函數(shù)獲取generator的下一個(gè)元素:


>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    next(g)
StopIteration
```
如果下一個(gè)元素不存在亚亲,就會(huì)報(bào)錯(cuò)彻采。而且next()函數(shù)太麻煩了腐缤,你可以使用for循環(huán):
```
>>> g = (x * x for x in range(10))
>>> for x in g:
 print(x)

 
0
1
4
9
16
25
36
49
64
81
```
> 提醒:如果上面這一步你沒(méi)有重新創(chuàng)建 g ,而是直接執(zhí)行for循環(huán)的話肛响,你會(huì)發(fā)現(xiàn)什么也打印不出來(lái)岭粤,因?yàn)間enerator中的指針已經(jīng)指到了generator的末尾了。

怎么樣特笋!生成器中的元素都是在你需要的時(shí)候才通過(guò)算法生成的剃浇,所以不會(huì)浪費(fèi)內(nèi)存空間。

但是猎物,有時(shí)候這個(gè)算法會(huì)特別復(fù)雜虎囚,用類似于列表生成式的for循環(huán)無(wú)法實(shí)現(xiàn),這時(shí)候你可以使用函數(shù)來(lái)實(shí)現(xiàn)這個(gè)算法蔫磨。

比如淘讥,斐波拉契數(shù)列,前兩個(gè)數(shù)已知质帅,后面的數(shù)都是它前面兩個(gè)數(shù)的和:

1适揉,1,2煤惩,3嫉嘀,5,8魄揉,13剪侮,21,34……

你無(wú)法用列表生成式的方式寫(xiě)出來(lái)洛退。但你可以使用函數(shù):
```
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(n)
        a, b = b, a+b
        n = n+1
    return 'done'
```
事實(shí)上瓣俯,這就是一個(gè)推導(dǎo)斐波那契數(shù)列的算法,跟generator很像兵怯,可以從第一個(gè)元素開(kāi)始彩匕,依次推導(dǎo)出后面的元素。

如果你想讓函數(shù)變成一個(gè)generator的話很簡(jiǎn)單媒区,只需要將上面的print(n)變成 yield b 就可以了驼仪。
```
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a+b
        n = n+1
    return 'done'
```
這樣fib()函數(shù)返回的就是一個(gè)generator對(duì)象了:
```
>>> f = fib(7)
>>> f
<generator object fib at 0x1057af0a0>
```
這時(shí)比較難以理解的一點(diǎn)是,此時(shí)generator的執(zhí)行流程跟函數(shù)的執(zhí)行流程不一樣袜漩。函數(shù)是順序執(zhí)行的绪爸,遇到return或者函數(shù)末尾時(shí)會(huì)返回,而通過(guò)函數(shù)來(lái)生成generator的時(shí)候宙攻,執(zhí)行流程就不一樣了奠货。generator會(huì)在遇到next()的時(shí)候執(zhí)行,遇到y(tǒng)ield的時(shí)候返回座掘,再次執(zhí)行時(shí)递惋,從上一次返回的yield語(yǔ)句繼續(xù)執(zhí)行柔滔。

舉個(gè)例子:
```
def test():
 print('first')
 yield 1
 print('second')
 yield 2
 print('third')
 yield 3
```
首先得到generator對(duì)象,然后通過(guò)next()函數(shù)不斷地得到下一個(gè)返回值:
```
>>> t = test()
>>> next(t)
first
1
>>> next(t)
second
2
>>> next(t)
third
3
>>> next(t)
Traceback (most recent call last):
  File "<pyshell#50>", line 1, in <module>
    next(t)
StopIteration
```
這里的test不是普通的函數(shù)萍虽,而是一個(gè)generator廊遍。它在執(zhí)行過(guò)程中,遇到y(tǒng)ield就中斷贩挣,并返回yield后面跟著的值喉前,下次又從中斷的地方繼續(xù)執(zhí)行。

再回到fib的例子中王财,我們第一次調(diào)用next()的時(shí)候卵迂,fib函數(shù)開(kāi)始執(zhí)行,它在yield的地方返回b的值绒净,也就是1见咒,然后它就中斷了,等待著你第二次調(diào)用next()挂疆,你第二次調(diào)用next()的時(shí)候改览,它從上次中斷的地方開(kāi)始執(zhí)行,a = b缤言,b = a + b宝当,n = n + 1,緊接著又遇到了yield了胆萧,又返回b庆揩,并中斷。然后你第三次調(diào)用next()跌穗,它又從上次中斷的地方開(kāi)始執(zhí)行……

當(dāng)然我們一般情況下不會(huì)直接調(diào)用next()订晌,我們通常使用for循環(huán):
```
>>> for n in fib(6):
            print(n)

1
1
2
3
5
8
```
不過(guò)需要注意的一點(diǎn)是,使用for循環(huán)你是得不到最終函數(shù)return的結(jié)果的蚌吸。如果你想得到return值锈拨,你需要捕獲StopIteration錯(cuò)誤,返回值包含在StopIteration的value中:
```
>>> g = fib(6)
>>> while true:
            try:
                x = next(g)
                print('g:', x)
            except StopIteration as e:
                print('函數(shù)返回值:', e.value)
                break;

g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
函數(shù)返回值: done
```
捕獲錯(cuò)誤會(huì)在后面講羹唠。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奕枢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肉迫,更是在濱河造成了極大的恐慌验辞,老刑警劉巖稿黄,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喊衫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杆怕,警方通過(guò)查閱死者的電腦和手機(jī)族购,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)壳贪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人寝杖,你說(shuō)我怎么就攤上這事违施。” “怎么了瑟幕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵磕蒲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我只盹,道長(zhǎng)辣往,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任殖卑,我火速辦了婚禮站削,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘孵稽。我一直安慰自己许起,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布菩鲜。 她就那樣靜靜地躺著园细,像睡著了一般。 火紅的嫁衣襯著肌膚如雪接校。 梳的紋絲不亂的頭發(fā)上珊肃,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音馅笙,去河邊找鬼伦乔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛董习,可吹牛的內(nèi)容都是我干的烈和。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼皿淋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼招刹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起窝趣,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤疯暑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后哑舒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體妇拯,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了越锈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仗嗦。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖甘凭,靈堂內(nèi)的尸體忽然破棺而出稀拐,到底是詐尸還是另有隱情,我是刑警寧澤丹弱,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布德撬,位于F島的核電站,受9級(jí)特大地震影響躲胳,放射性物質(zhì)發(fā)生泄漏砰逻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一泛鸟、第九天 我趴在偏房一處隱蔽的房頂上張望蝠咆。 院中可真熱鬧,春花似錦北滥、人聲如沸刚操。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)菊霜。三九已至,卻和暖如春济赎,著一層夾襖步出監(jiān)牢的瞬間鉴逞,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工司训, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留构捡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓壳猜,卻偏偏與公主長(zhǎng)得像勾徽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子统扳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容