您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關Python生成器是什么,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
一、 生成器(generator)概念
生成器是一個特殊的迭代器,它保存的是算法,每次調用next()或send()就計算出下一個元素的值,直到計算出最后一個元素,沒有更多的元素時,拋出StopIteration。生成器有兩種類型,一種是生成器表達式(又稱為生成器推導),一種是生成器函數。
二、 生成器表達式
生成器表達式是通過一個Python表達式語句去計算一系列數據,但生成器定義的時候數據并沒有生成,而是返回一個對象,這個對象只有在需要的時候才根據表達式計算當前需要返回的數據:
生成器表達式來源于迭代和列表解析(列表解析后面章節介紹)的組合,生成器和列表解析類似,但是它使用小括號而不是中括號。生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表;
生成器表達式的語法如下:
(expr for iter_var in iterable) (expr for iter_var in iterable if cond_expr)
其中:
expr為計算 生成器元素值的表達式
for iter_var in iterable iter_var:表示針對在可迭代對象iter_var中的每個元素進行表達式運算
if cond_exp:表示可迭代對象中的元素需要滿足指定條件才會參與表達式運算
說明
直接在一對既有的小括號內(如在函數調用中)使用生成器表達式時,無需再添加一對小括號。例如:sum(i ** 2 for i in range(10));
生成器表達式與列表解析的語法非常象,由于涉及部分相關的函數,在列表解析相關的章節老猿再回頭介紹一下生成器表達式有關的內容。
三、 生成器函數
生成器函數是一種語句中包含yield關鍵詞的特殊的函數,它本身是一個迭代器,外部需要訪問該迭代器數據的代碼通過調用next函數(或迭代器的__next__方法)或send方法,觸發函數執行計算并通過yield返回一個計算結果數據,返回數據后該函數立即停止執行,函數狀態會保存在本地變量中,直到外部下次調用再激活,從上次停止執行部分開始執行。
1、 關于生成器函數與調用方的執行過程解析
生成器函數定義示意代碼(非可執行代碼)如下:
def fun():
初始化
循環:
計算得到k
nRet=yield k
其他循環代碼
上面代碼示意表示:生成器函數運行時計算得到結果k通過yield返回數據k給調用方,返回k給調用方之后,生成器函數停止執行,yield的調用執行結果并沒有返回給生成器函數, nRet的賦值也并沒有執行,等待下次調用后,再返回yield本身的執行結果,并繼續后續循環代碼,直到再次執行yield。
老猿通過驗證理解有幾個細節在此說明一下:
a) yield函數的執行是一條語句,但實際執行時該語句被分解成兩部分,第一部分是將計算結果k返回給send或next調用處(下稱觸發方),保存當前環境,暫停執行,另一部分就是恢復當前環境,返回yield本身的執行結果給生成器函數的調用處,并繼續往下執行后續循環。每次調用yield時,除了第一次是從第一部分執行,后續都是從第二部分開始執行。
b) yield返回值(nRet記下來的值)在觸發方為next(含__next__方法,下同)時,為None,如果觸發方是send,則該值為send方法參數中的發送值;
c) 生成器函數在調用時只是生成一個生成器實例,并沒有真正執行,真正執行只有第一次通過next觸發時才會進入函數執行,注意第一次觸發不能是send方式觸發。
2) 調用生成器代碼示意
def main():
初始化
f= fun() next(f)
循環:
其他循環代碼
nRet=send(x)
其他循環代碼
上面代碼示意表示:調用方執行自身初始化,然后進行生成器函數的初始化,然后執行循環迭代訪問生成器函數的數據。
同樣有幾個細節老猿在此說明一下:
a) f= fun(),這個語句不會進入函數執行,只是生成一個生成器實例f
b) 第一個next調用只有循環代碼中使用send觸發時才需要,如果循環中用next則無需先執行一次send;
c) 第一個next執行時會觸發調用生成器函數,從生成器第一行代碼開始執行;后續的next或send執行,不再執行生成器函數的初始化部分,只是從yield的第二部分開始執行,第二部分執行時應該在生成器函數的循環迭代代碼內,因此此后執行還是在生成器函數的循環代碼內循環,直到遇到yield語句,執行完yield語句的第一部分邏輯掛起函數等待再次出發;
d) nRet記錄的返回值就是生成器函數yield后面返回給觸發方的數據。
2、 下面是一個老猿編寫的模擬存快遞包裹的生成器函數及其調用代碼,每執行一次存包裹的函數就掛起,主程序等待確認是否繼續循環,如果不繼續則退出,代碼如下:
import random def PutPackage(): print(‘PutPackage start…’) nRet = 123 while True: if nRet<1 : break print(‘PutPackage:Before Yield…’) nRet = yield ’ PutPackage’+str(nRet) #返回字符串PutPackage+上次循環yield的返回值 print(‘PutPackage:After Yield, nRet=’,nRet) if not nRet: continue def mainf(): print(‘mainf start call PutPackage …’) vPutPackage=PutPackage() #只是返回生成器generator對象 bBreak = False print(‘mainf start call next …’) nRet=next(vPutPackage) #生成器初始化 print(‘mainf end call next,nRet=’,nRet) while True: if bBreak: try: #為什么要捕獲異常? vPutPackage.send(-1) #觸發—1給生成器函數提示函數退出 except StopIteration:pass break print(‘mainf loop start call send …’) nRet=vPutPackage.send(random.randint(10000,99999)) #產生一個隨機包裹編號觸發給生成器函數 print(‘mainf loop after call send ,nRet=’,nRet) sConfirm=input(“是否準備結束存件取件循環(Y或y是,否則繼續循環):”) if sConfirm.strip().upper()==‘Y’: bBreak=True print("\n") mainf()
執行結果如下,大家對照前面的執行過程解析理解一下:
mainf start call PutPackage … mainf start call next … PutPackage start… PutPackage:Before Yield… mainf end call next,nRet= PutPackage123 mainf loop start call send … PutPackage:After Yield, nRet= 66468 PutPackage:Before Yield… mainf loop after call send ,nRet= PutPackage66468
是否準備結束存件取件循環(Y或y是,否則繼續循環):n
mainf loop start call send … PutPackage:After Yield, nRet= 22204 PutPackage:Before Yield… mainf loop after call send ,nRet= PutPackage22204
是否準備結束存件取件循環(Y或y是,否則繼續循環):y
PutPackage:After Yield, nRet= -1
上述代碼中為什么要捕獲異常?這是因為最后一個send(-1)時,是從yield第二部分執行,執行到循環“if nRet<1 : break”語句就會終止循環,不會再通過yield向觸發方返回值,此時send執行就會出現迭代結束的異常。
3、 生成器函數的其他說明
Python使用生成器對延遲操作提供了支持。所謂延遲操作,是指在需要的時候才產生結果,而不是立即產生結果。這有利于節省內存,特別是生成器進行科學計算時很有用;
生成器就是迭代器,除了next方法外,也可以通過for循環來遍歷出生成器中的內容;
生成器除了前面介紹的__next__、send方法外,還有throw、close方法:
a) throw(type[, value[, traceback]]):該方法在生成器暫停的位置引發 type 類型的異常,并返回該生成器函數所產生的下一個值。 如果生成器沒有產生下一個值就退出,則將引發 StopIteration 異常。 如果生成器函數沒有捕獲傳入的異常,或引發了另一個異常,則該異常會被傳播給調用者。該方法可以解決上面案例捕獲異常的處理
b) close():在生成器函數暫停的位置引發 GeneratorExit。 如果之后生成器函數正常退出、關閉或引發 GeneratorExit(由于未捕獲該異常) 則關閉并返回其調用者。 如果生成器產生了一個值,關閉會引發 RuntimeError。 如果生成器引發任何其他異常,它會被傳播給調用者。 如果生成器已經由于異常或正常退出則 close() 不會做任何事。通過觸發方調用close方法可以直接關閉生成器,而不需要象上面案例一樣在生成器函數內判斷send發送的數據來進行退出。
上述就是小編為大家分享的Python生成器是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。