透過列表生成式,我們可以直接建立一個清單。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們只需要存取前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。所以,如果列表元素可以依照某種演算法推導出來,那我們是否可以在循環的過程中不斷推導出後續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
要建立一個生成器,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[ ]改成( )
創建L和G的區別僅在於最外層的[ ]和( ),L是一個列表,而G是一個生成器。我們可以直接列印出L的每一個元素,但我們要怎麼列印出G的每一個元素呢?如果要一個一個列印出來,可以透過next()函數得到生成器的下一個回傳值:
#產生器保存的是演算法,每次都呼叫next( G),就計算出G的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的異常。當然,這種不斷呼叫next()實在是太變態了,正確的方法是使用for循環,因為生成器也是可迭代物件。所以,我們創建了一個生成器後,基本上永遠不會呼叫next(),而是透過for循環來迭代它,並且不需要關心StopIteration異常。
generator非常強大。如果推算的演算法比較複雜,用類似列表產生式的for迴圈無法實現的時候,還可以用函數來實作。
例如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任一個數都可由前兩個數相加得到:
1 , 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:
#仔細觀察,可以看出,fib函數其實是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出後續任意的元素,這種邏輯其實非常類似generator。
也就是說,上面的函數和generator只有一步之遙。要把fib函數變成generator,只要要把print(b)改為yield b就可以了:
但是用for迴圈呼叫generator時,發現拿不到generator的return語句的回傳值。如果想要拿到回傳值,必須捕獲StopIteration錯誤,回傳值包含在StopIteration的value中:
使用next函數
總結
·凡是可作用於for迴圈的物件都是Iterable型別;
·凡是可作用於next()函數的物件都是Iterator型別
·集合資料型態如list、dict、str等是Iterable但不是Iterator,不過可以透過iter()函數取得一個Iterator物件。
·目的是在使用集合的時候,減少佔用的內容。
1.函數引用
這個例子中,函數line與變數a,b構成閉包。在建立閉包的時候,我們透過line_conf的參數a,b說明了這兩個變數的取值,這樣,我們就確定了函數的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數a,b,就可以得到不同的直線表達函數。由此,我們可以看到,閉包也具有提高程式碼可重複使用性的作用。
如果沒有閉包,我們需要每次建立直線函數的時候同時說明a,b,x。這樣,我們就需要更多的參數傳遞,也減少了程式碼的可移植性
#學習過程中遇到什麼問題或想取得學習資源的話,歡迎加入學習交流群組
626062078,我們一起學Python!
以上是Python-生成器詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!