首頁  >  文章  >  後端開發  >  Python-生成器詳解

Python-生成器詳解

巴扎黑
巴扎黑原創
2017-06-23 15:10:061425瀏覽

1.什麼是生成器

透過列表生成式,我們可以直接建立一個清單。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們只需要存取前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。所以,如果列表元素可以依照某種演算法推導出來,那我們是否可以在循環的過程中不斷推導出後續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。

2.建立生成器方法

方法一

要建立一個生成器,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[ ]改成( )

創建L和G的區別僅在於最外層的[ ]和( ),L是一個列表,而G是一個生成器。我們可以直接列印出L的每一個元素,但我們要怎麼列印出G的每一個元素呢?如果要一個一個列印出來,可以透過next()函數得到生成器的下一個回傳值:


運行結果:

執行結果:

#產生器保存的是演算法,每次都呼叫next( G),就計算出G的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的異常。當然,這種不斷呼叫next()實在是太變態了,正確的方法是使用for循環,因為生成器也是可迭代物件。所以,我們創建了一個生成器後,基本上永遠不會呼叫next(),而是透過for循環來迭代它,並且不需要關心StopIteration異常。

方法2

generator非常強大。如果推算的演算法比較複雜,用類似列表產生式的for迴圈無法實現的時候,還可以用函數來實作。

例如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任一個數都可由前兩個數相加得到:

1 , 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:


運行結果:

#仔細觀察,可以看出,fib函數其實是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出後續任意的元素,這種邏輯其實非常類似generator。

也就是說,上面的函數和generator只有一步之遙。要把fib函數變成generator,只要要把print(b)改為yield b就可以了:


#運行結果:

在上面fib的例子,我們在循環過程中不斷呼叫yield,就會不斷中斷。當然要給循環設定一個條件來退出循環,不然就會產生一個無限數列出來。同樣的,把函數改成generator後,我們基本上從來不會用next()來取得下一個回傳值,而是直接使用for迴圈來迭代:

執行結果:

但是用for迴圈呼叫generator時,發現拿不到generator的return語句的回傳值。如果想要拿到回傳值,必須捕獲StopIteration錯誤,回傳值包含在StopIteration的value中:


運行結果:
Python-生成器詳解

使用next函數


#執行結果:


##使用__next__()方法

執行結果:

使用send

運行結果:
#4.實作多任務
模擬多任務實作方式之一:協程

運行結果:

#總結

產生器是這樣一個函數,它記住上一次返回時在函數體中的位置。對生成器函數的第二次(或第n次)呼叫跳轉至該函數中間,而上次呼叫的所有局部變數都保持不變。

生成器不僅「記住」了它資料狀態;生成器還「記住」了它在流控制構造(在命令式程式設計中,這種構造不只是資料值)中的位置。

生成器的特性:

1.當節約記憶體

2.迭​​代到下次的呼叫時,所使用的參數都是第一次保留下的,即是說,在整個所有函數呼叫的參數都是第一次所呼叫時保留的,而不是新創建的

5.迭代器

迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的物件。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問結束。迭代器只能往前不會後退。

1.可迭代物件

以直接作用於for迴圈的資料型別有以下幾種:

一類別是集合資料型別,如list、tuple、dict 、set、str等;
一類是generator,包括生成器和帶有yield的generator function。
這些可以直接作用於for迴圈的物件統稱為可迭代物件:Iterable。
2.判斷是否可以迭代
可以使用isinstance()判斷物件是否為Iterable物件:

在執行結果:


而生成器不但可以作用於for循環,還可以被next()函數不斷呼叫並傳回下一個值,直到最後拋出StopIteration錯誤表示無法繼續回傳下一個值了。
3.迭代器
可以被next()函數呼叫並不斷傳回下一個值的物件稱為迭代器:Iterator。

執行結果:


#4.iter()函數
#產生器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函式:
############執行結果:####### #######

總結

·凡是可作用於for迴圈的物件都是Iterable型別;

·凡是可作用於next()函數的物件都是Iterator型別

·集合資料型態如list、dict、str等是Iterable但不是Iterator,不過可以透過iter()函數取得一個Iterator物件。

·目的是在使用集合的時候,減少佔用的內容。

6.閉包

1.函數引用


#運行結果:

圖解:

2.什麼是閉包


運行結果:

3.看一個閉包的實際範例:


運行結果:

這個例子中,函數line與變數a,b構成閉包。在建立閉包的時候,我們透過line_conf的參數a,b說明了這兩個變數的取值,這樣,我們就確定了函數的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數a,b,就可以得到不同的直線表達函數。由此,我們可以看到,閉包也具有提高程式碼可重複使用性的作用。

如果沒有閉包,我們需要每次建立直線函數的時候同時說明a,b,x。這樣,我們就需要更多的參數傳遞,也減少了程式碼的可移植性

#學習過程中遇到什麼問題或想取得學習資源的話,歡迎加入學習交流群組
626062078,我們一起學Python!

以上是Python-生成器詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn