Python基礎(chǔ)手冊20——生成器

一、生成器

Python 提供更多的對延遲的支持——它提供了工具在需要的時候才產(chǎn)生結(jié)果,而不是立即產(chǎn)生結(jié)果。有兩種語言結(jié)構(gòu)盡可能的延遲結(jié)果創(chuàng)建。

生成器函數(shù): 編寫為常規(guī)的 def 語句,但是使用 yield 語句一次返回一個結(jié)果,在每個結(jié)果之間掛起和繼續(xù)它們的狀態(tài)。

生成器表達式: 類似于列表解析,但是,它們返回一個按需產(chǎn)生結(jié)果的對象,而不是構(gòu)建一個結(jié)果列表。

由于二者都不會一次性的構(gòu)建一個列表,它們節(jié)省了內(nèi)存空間,并且允許計算時間分散到各個結(jié)果請求。

1、生成器函數(shù):yield

我們可以編寫返回一個值并隨后從其退出的地方繼續(xù)執(zhí)行的函數(shù)。這樣的函數(shù)叫做生成器函數(shù)。因為它們隨著時間產(chǎn)生值的一個序列。

一般來說,生成器函數(shù)和常規(guī)函數(shù)一樣,并且,實際上也是用常規(guī)的 def 語句編寫的。只是不是使用 return 語句返回一個值,而是使用 yield 語句,當創(chuàng)建時,它們自動實現(xiàn)迭代協(xié)議,以便在迭代環(huán)境中被迭代。

這個函數(shù)在每次循環(huán)時都會產(chǎn)生一個值,將其返還給它的調(diào)用者。當它被暫停后,它的上一個狀態(tài)保存了下來,并且在 yield 語句之后控制器馬上被回收。

直接調(diào)用生成器函數(shù)會產(chǎn)生一個生成器對象,它支持迭代器協(xié)議,也就是說,生成器對象有一個 __next__ 方法,它可以開始這個函數(shù),或者從它上次 yield 值后的地方恢復,并且在得到一系列的值的最后一個時,產(chǎn)生 StopIteration 異常。

next() 內(nèi)置函數(shù)會為我們調(diào)用一個對象的 __next__() 方法。

相較于傳統(tǒng)函數(shù),只能一次返回所需的所有值,生成器在內(nèi)存使用和性能方面都更好,他們允許函數(shù)避免臨時再做所有的工作,當結(jié)果的列表很大或者在處理每一個結(jié)果都需要很多時間時,這一點尤其有用。生成器將在 loop 迭代中處理一系列值的時間分布開來。有了生成器,函數(shù)變量就能進行自動的保存和恢復。


狀態(tài)掛起

和返回一個值并退出的常規(guī)函數(shù)不同,生成器函數(shù)自動在生成值的時刻掛起并等待繼續(xù)函數(shù)的執(zhí)行。因此,它們對于計算一系列值以及在類中手動保存和恢復狀態(tài)都很有用。由于生成器函數(shù)在掛起時保存的狀態(tài)包含它們的整個本地作用域,當函數(shù)恢復時,它們的本地變量保持了信息并且可用。

生成器函數(shù)和常規(guī)函數(shù)之間的主要的代碼不同之處在于,生成器 yield 返回一個值,而不是 return 返回一個值。yield 語句掛起該函數(shù)并向調(diào)用者發(fā)送回一個值,但是,保留足夠的狀態(tài)以使得函數(shù)能夠從它離開的地方繼續(xù)。當繼續(xù)時,函數(shù)在上一個 yield 返回后立即繼續(xù)執(zhí)行。從函數(shù)的角度來看,這允許其代碼隨著時間產(chǎn)生一系列的值,而不是一次計算它們并在諸如列表的內(nèi)容中返回它們。


迭代協(xié)議

生成器函數(shù)與 Python 中的迭代協(xié)議的概念密切相關(guān)。可迭代對象的迭代器(自己的或者靠 iter() 生成的)定義了一個__next__() 方法,它要么返回迭代中的下一項,或者引發(fā)一個特殊的 StopIteration 異常來終止迭代。

要支持這一協(xié)議,函數(shù)需要包含一條 yield 語句,該語句特別編譯為生成器。當調(diào)用函數(shù)時,它們返回一個可迭代對象,該對象擁有自己自動創(chuàng)建的 __next__() 方法。生成器函數(shù)也可能有一條 return 語句,總是在 def 語句塊的末尾,直接終止值的生成。從技術(shù)上講,可以在任何常規(guī)函數(shù)退出執(zhí)行之后,引發(fā)一個 StopIteration 異常來實現(xiàn)。從調(diào)用者的角度來看,生成器的 __next__() 方法繼續(xù)函數(shù)并且運行到下一個 yield 結(jié)果返回或引發(fā)一個 StopItertio 異常。

直接效果就是生成器函數(shù),編寫為包含 yield 語句的 def 語句,自動地支持迭代協(xié)議,并且可用在任何迭代環(huán)境中以隨著時間并根據(jù)需要產(chǎn)生結(jié)果。


2、擴展生成器函數(shù)協(xié)議:send() 和 next()

send() 方法生成一系列結(jié)果的下一個元素,這一點就像 __next__() 方法一樣,但是它提供了一種調(diào)用者與生成器之間進行通信的方法,從而能夠影響它的操作。

生成器可以通過調(diào)用 G.send(value) 方法將參數(shù) value 發(fā)送給生成器 G。之后恢復生成器的代碼,并且生成器中的 yield 表達式返回了發(fā)送過來的值。如果調(diào)用了正常的G.__next__() 方法,yield 返回 None。

從技術(shù)上講,yield 現(xiàn)在是一個表達式的形式,可以返回傳入的元素,而不是一個語句。表達式必須包含在括號中,除非它是賦值語句右邊的唯一一項。

next() 方法為內(nèi)置函數(shù),但是生成器方法 send() 必須作為生成器對象的方法來調(diào)用。因為這些生成器方法只是在內(nèi)置的生成器對象上實現(xiàn),而 __next__() 方法應(yīng)用于所有的可迭代對象(包括內(nèi)置類型和與用戶自定義類型)。


3、生成器表達式

從語法上來講,生成器表達式就像一般的列表解析一樣,但是它們是擴在圓括號中而不是方括號中的。

編寫一個列表基本上等同于:在一個 list() 內(nèi)置調(diào)用中包含一個生成器表達式以迫使其一次生成列表中所有的結(jié)果。

生成器表達式不是在內(nèi)存中構(gòu)建結(jié)果,而是返回一個生成器對象,這個對象將會支持迭代協(xié)議并在任意的迭代語境的操作中。

我們一般不會機械的使用 next() 迭代器來操作生成器表達式,因為 for 循環(huán)會自動觸發(fā)。

如果生成器表達式大體上可以認為是對內(nèi)存空間的優(yōu)化,它們不需要像方括號的列表解析一樣,一次構(gòu)造出整個結(jié)果列表。它們在實際中運行起來可能稍慢一些,但是它們對于非常大的結(jié)果集合的運算來說是最優(yōu)的選擇。


4、生成器函數(shù) VS 生成器表達式

同樣的迭代往往可以用一個生成器函數(shù)或一個生成器表達式編寫。等價的生成器函數(shù)需要略微多一些的代碼,但是,作為一個多語句的函數(shù),如果需要的話,它能夠編寫更多的邏輯并使用更的狀態(tài)信息。


5、生成器是單迭代對象

生成器函數(shù)和生成器表達式自身就是迭代器,并由此只支持一次活躍迭代。因為生成器的迭代器是生成器自身,所以在一個生成器上調(diào)用 iter() 沒有任何效果。

如果你手動的使用多個迭代器來迭代結(jié)果流,他們將會指向相同的位置。

一旦任何迭代器運行到完成,所有的迭代器都將用盡,我們必須產(chǎn)生一個新的生成器以再次開始迭代。

這與某些內(nèi)置類型的行為不同,它們支持創(chuàng)建多個迭代器并且在一個活動迭代器中傳遞并返映它們的原處修改。


《Python基礎(chǔ)手冊》系列:

Python基礎(chǔ)手冊 1 —— Python語言介紹
Python基礎(chǔ)手冊 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊 3 —— Python解釋器
Python基礎(chǔ)手冊 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊 5 —— 標識符和關(guān)鍵字
Python基礎(chǔ)手冊 6 —— 操作符
Python基礎(chǔ)手冊 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊 8 —— Python對象
Python基礎(chǔ)手冊 9 —— 數(shù)字類型
Python基礎(chǔ)手冊10 —— 序列(字符串)
Python基礎(chǔ)手冊11 —— 序列(元組&列表)
Python基礎(chǔ)手冊12 —— 序列(類型操作)
Python基礎(chǔ)手冊13 —— 映射(字典)
Python基礎(chǔ)手冊14 —— 集合
Python基礎(chǔ)手冊15 —— 解析
Python基礎(chǔ)手冊16 —— 文件
Python基礎(chǔ)手冊17 —— 簡單語句
Python基礎(chǔ)手冊18 —— 復合語句(流程控制語句)
Python基礎(chǔ)手冊19 —— 迭代器
Python基礎(chǔ)手冊20 —— 生成器
Python基礎(chǔ)手冊21 —— 函數(shù)的定義
Python基礎(chǔ)手冊22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊25 —— 裝飾器
Python基礎(chǔ)手冊26 —— 錯誤 & 異常
Python基礎(chǔ)手冊27 —— 模塊
Python基礎(chǔ)手冊28 —— 模塊的高級概念
Python基礎(chǔ)手冊29 —— 包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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