神秘而強大的Python生成器精講

2021-09-25 02:58:45 字數 4592 閱讀 7203

一、 生成器(generator)概念

生成器是乙個特殊的迭代器,它儲存的是演算法,每次呼叫next()或send()就計算出下乙個元素的值,直到計算出最後乙個元素,沒有更多的元素時,丟擲stopiteration。生成器有兩種型別,一種是生成器表示式(又稱為生成器推導),一種是生成器函式。

、 生成器表示式

生成器表示式是通過乙個python表示式語句去計算一系列資料,但生成器定義的時候資料並沒有生成,而是返回乙個物件,這個物件只有在需要的時候才根據表示式計算當前需要返回的資料:

生成器表示式**於迭代和列表解析(列表解析後面章節介紹)的組合,生成器和列表解析類似,但是它使用小括號而不是中括號。生成器返回按需產生結果的乙個物件,而不是一次構建乙個結果列表;

生成器表示式的語法如下:

(expr for iter_var in iterable)

(expr for iter_var in iterable if cond_expr)

其中:

expr為計算 生成器元素值的表示式生成器函式定義示意**(非可執行**)如下:

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方式觸發。

呼叫生成器**示意

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

上述**中為什麼要捕獲異常?這是因為最後乙個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 生成器作用 Python生成器

生成器介紹 在函式內部包含yield關鍵字,那麼該函式執行的結果是生成器,生成器就是迭代器。生成器的功能 把函式結果做成迭代器 以一種優雅的方式封裝好iter,next 提供了一種自己定義迭代器的方式。使用生成器建立乙個迭代器 def a print a yield 11 使用yield,執行後返回...

python生成器好處 Python生成器筆記

python中三大器有迭代器,生成器,裝飾器,本文主要講述生成器。主要從生成器的概念,本質,以及yield關鍵字的使用執行過程。本質 生成器是一類特殊的迭代器,使用了yield關鍵字的函式不再是函式,而是生成器。使用了yield的函式就是生成器 1.yield關鍵字有兩點作用 1.1 yield語句...

python生成器函式 Python 生成器函式

一 生成器 生成器指的是生成器物件,可由生成器表示式得到,也可使用 yield 關鍵字得到乙個生成器函式,呼叫這個函式得到乙個生成器物件 生成器物件,是乙個可迭代物件,是乙個迭代器 生成器物件,是延遲計算 惰性求值的 1.1 生成器函式 函式體重包含 yield 語句的函式,就是生成器函式,呼叫後返...