深入學(xué)習(xí)python的yield和generator
前言
沒(méi)有用過(guò)的東西,沒(méi)有深刻理解的東西很難說(shuō)自己會(huì),而且被別人一問(wèn)必然破綻百出。雖然之前有接觸過(guò)python協(xié)程的概念,但是只是走馬觀花,這兩天的一次交談中,別人問(wèn)到了協(xié)程,頓時(shí)語(yǔ)塞,死活想不起來(lái)曾經(jīng)看過(guò)的東西,之后突然想到了yield,但為時(shí)已晚,只能說(shuō)概念不清,所以本篇先縷縷python的生成器和yield關(guān)鍵字。
什么是生成器
1、生成器是一個(gè)特殊的程序,可以被用作控制循環(huán)的迭代行為
2、生成器類(lèi)似于返回值為數(shù)組的一個(gè)函數(shù),這個(gè)函數(shù)可以接收參數(shù),可以被調(diào)用,但是,不同于一般的函數(shù)會(huì)一次性返回包含了所有數(shù)值的數(shù)組,生成器一次只產(chǎn)生一個(gè)值,這樣消耗的內(nèi)粗?jǐn)?shù)量大大減少,而且允許調(diào)用函數(shù)可以很快的開(kāi)始處理前幾個(gè)返回值。因此,生成器看起來(lái)像一個(gè)函數(shù)但是表現(xiàn)的卻像一個(gè)迭代器。
python中的生成器
python提供了兩種基本的方式。
1)、生成器函數(shù):也是用def來(lái)定義,利用關(guān)鍵字yield一次返回一個(gè)結(jié)果,阻塞,重新開(kāi)始
2)、生成器表達(dá)式:返回一個(gè)對(duì)象,這個(gè)對(duì)象只有在需要的時(shí)候才產(chǎn)生結(jié)果
下面詳細(xì)講解。
1、生成器函數(shù)
為什么叫生成器函數(shù)?因?yàn)樗S著時(shí)間的推移生成了一個(gè)數(shù)值隊(duì)列。一般的函數(shù)在執(zhí)行完畢之后會(huì)返回一個(gè)值然后退出,但是生成器函數(shù)會(huì)自動(dòng)掛起,然后重新拾起繼續(xù)執(zhí)行,他會(huì)利用yield關(guān)鍵字關(guān)起函數(shù),給調(diào)用者返回一個(gè)值,同時(shí)保留了當(dāng)前的足夠多的狀態(tài),可以使函數(shù)繼續(xù)執(zhí)行。生成器和迭代協(xié)議是密切相關(guān)的,可迭代的對(duì)象都有一個(gè)__next()__成員方法,這個(gè)方法要么返回迭代的下一項(xiàng),要么引起異常結(jié)束迭代。
為了支持迭代協(xié)議,擁有yield語(yǔ)句的函數(shù)被編譯為生成器,這類(lèi)函數(shù)被調(diào)用時(shí)返回一個(gè)生成器對(duì)象,返回的對(duì)象支持迭代接口,即成員方法__next()__繼續(xù)從中斷處執(zhí)行執(zhí)行。
看下面的例子:
# codes def create_counter(n): print "create counter" while True: yield n print 'increment n' n += 1 cnt = create_counter(2) print cnt print next(cnt) print next(cnt) # output <generator object create_counter at 0x0000000001D141B0> create counter 2 increment n 3
分析一下這個(gè)例子:
- 在create_counter函數(shù)中出現(xiàn)了關(guān)鍵字yield,預(yù)示著這個(gè)函數(shù)每次只產(chǎn)生一個(gè)結(jié)果值,這個(gè)函數(shù)返回一個(gè)生成器(通過(guò)第一行輸出可以看出來(lái)),用來(lái)產(chǎn)生連續(xù)的n值
- 在創(chuàng)造生成器實(shí)例的時(shí)候,只需要像普通函數(shù)一樣調(diào)用就可以,但是這個(gè)調(diào)用卻不會(huì)執(zhí)行這個(gè)函數(shù),這個(gè)可以通過(guò)輸出看出來(lái)
- next()函數(shù)將生成器對(duì)象作為自己的參數(shù),在第一次調(diào)用的時(shí)候,他執(zhí)行了create_counter()函數(shù)到y(tǒng)ield語(yǔ)句,返回產(chǎn)生的值2
- 我們重復(fù)的調(diào)用next()函數(shù),每次他都會(huì)從上次被掛起的地方開(kāi)始執(zhí)行,直到再次遇到了yield關(guān)鍵字
為了更加深刻的理解,我們?cè)倥e一個(gè)例子。
#coding def cube(n): for i in range(n): yield i ** 3 for i in cube(5): print i #output 0 1 8 27 64
所以從理解函數(shù)的角度出發(fā)我們可以將yield類(lèi)比為return,但是功能確實(shí)完全不同,在for循環(huán)中,會(huì)自動(dòng)遵循迭代規(guī)則,每次調(diào)用next()函數(shù),所以上面的結(jié)果不難理解。
2、生成器表達(dá)式
生成器表達(dá)式來(lái)自于迭代和列表解析的組合,關(guān)于列表解析的概念和用法可以參見(jiàn)我之前的博客,生成器表達(dá)式和列表解析類(lèi)似,但是他使用尖括號(hào)而不是方括號(hào)括起來(lái)的。如下代碼:
>>> # 列表解析生成列表 >>> [ x ** 3 for x in range(5)] [0, 1, 8, 27, 64] >>> >>> # 生成器表達(dá)式 >>> (x ** 3 for x in range(5)) <generator object <genexpr> at 0x000000000315F678> >>> # 兩者之間轉(zhuǎn)換 >>> list(x ** 3 for x in range(5)) [0, 1, 8, 27, 64]
就操作而言,生成器表如果使用大量的next()函數(shù)會(huì)顯得十分不方便,for循環(huán)會(huì)自動(dòng)出發(fā)next函數(shù),所以可以按下面方式使用:
>>> for n in (x ** 3 for x in range(5)): print('%s, %s' % (n, n * n)) 0, 0 1, 1 8, 64 27, 729 64, 4096 >>>
兩者比較
一個(gè)迭代既可以被寫(xiě)成生成器函數(shù),也可以被協(xié)程生成器表達(dá)式,均支持自動(dòng)和手動(dòng)迭代。而且這些生成器只支持一個(gè)active迭代,也就是說(shuō)生成器的迭代器就是生成器本身。
總結(jié)
想起了初中時(shí)候老師經(jīng)常說(shuō)的,眼觀千遍,不如手動(dòng)一遍。
相關(guān)文章
python調(diào)用pymssql包操作SqlServer數(shù)據(jù)庫(kù)的實(shí)現(xiàn)
本文主要介紹了python調(diào)用pymssql包操作SqlServer數(shù)據(jù)庫(kù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06使用Python PIL庫(kù)讀取文件批量處理圖片大小實(shí)現(xiàn)
這篇文章主要為大家介紹了使用Python PIL庫(kù)讀取文件批量處理圖片大小實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07分享python機(jī)器學(xué)習(xí)中應(yīng)用所產(chǎn)生的聚類(lèi)數(shù)據(jù)集方法
本文根據(jù) 機(jī)器學(xué)習(xí)中常用的聚類(lèi)數(shù)據(jù)集生成方法 中的內(nèi)容進(jìn)行編輯實(shí)驗(yàn)和整理而得,有需要的朋友可以參考想,希望可以對(duì)大家在聚類(lèi)數(shù)據(jù)方面有所幫助2021-08-08Python+Pygame實(shí)戰(zhàn)之24點(diǎn)游戲的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了如何利用Python和Pygame實(shí)現(xiàn)24點(diǎn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04使用python提取html文件中的特定數(shù)據(jù)的實(shí)現(xiàn)代碼
python提供了SGMLParser類(lèi)用于html文件的解析。用戶(hù)只需從SGMLParser類(lèi)繼承子類(lèi),并在子類(lèi)中對(duì)html文件做具體處理2013-03-03零基礎(chǔ)學(xué)python應(yīng)該從哪里入手
在本篇文章里小編給大家分享的是一篇關(guān)于零基礎(chǔ)學(xué)python應(yīng)該從哪里入手的相關(guān)基礎(chǔ)內(nèi)容,需要的朋友們可以參考下。2020-08-08Python+OpenCV實(shí)現(xiàn)邊緣檢測(cè)與角點(diǎn)檢測(cè)詳解
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Python+OpenCV實(shí)現(xiàn)邊緣檢測(cè)與角點(diǎn)檢測(cè),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Python與OpenCV有一定的幫助,需要的可以參考一下2023-02-02