generator是一種函式或運算式,會回傳一種特殊的疊代器,稱為生成器疊代器。
Generator-iterators是惰性的:它們不會把values存在記憶體中,而是在需要時才_生成_這些值。
生成器函式看起來和一般函式沒什麼不同,但它包含一個或多個yield 運算式。
每個yield都會暫停程式碼的執行,並保存當下的執行狀態(包含所有區域變數和 try 敘述)。
當生成器恢復執行時,它會接續暫停前的狀態,這點和每次呼叫都會重置的一般函式不同。
生成器的建立方式和其他迴圈或遞迴函式很像,但需要一個yield運算式,我們稍後會深入探討。
舉例來說,一個函式可以回傳某串數字中每個數的_平方_。
以目前的寫法,必須先處理完所有輸入,才能回傳任何值:
>>> def squares(list_of_numbers):
... squares = []
... for number in list_of_numbers:
... squares.append(number ** 2)
... return squares
你可以像這樣把該函式改寫成生成器:
>>> def squares_generator(list_of_numbers):
... for number in list_of_numbers:
... yield number ** 2
這麼做的理由是,當你不需要_一次_取得所有值時,就會用到生成器。
這能節省記憶體和運算效能,因為只有你_當下正在處理_的值會被計算。
生成器(嚴格來說是generator-iterator,請見下方的注意事項)是一種iterator,在 Python 中只要是需要iterator或iterable的地方都能使用。
這包括需要iterable或iterator作為引數的_函式_或_物件_。
想更深入了解,請參閱如何在 Python 中建立疊代器。
生成器疊代器是疊代器的一個特殊子集。
Iterators是讓你能對_可疊代物件_進行迴圈的機制/協定。
生成器疊代器和常見 Python iterables回傳的疊代器行為非常相似,但有幾個重要差異值得注意:
indexes。
你無法用加法或減法取得前一個或後一個值,也不能使用中括號([])標記法或切片。len()函式使用,因為它們沒有長度。generator-iterator收集所有值時要小心!要使用squares_generator()生成器:
>>> squared_numbers = squares_generator([1, 2, 3, 4])
>>> for square in squared_numbers:
... print(square)
...
1
4
9
16
生成器中的值也可以透過next()函式來產生或存取。
next()會呼叫生成器物件的__next__()方法,將生成器程式碼「推進」或求值到它的yield運算式,接著這個運算式會「產生」或回傳該值。
>>> squared_numbers = squares_generator([1, 2])
>>> next(squared_numbers)
1
>>> next(squared_numbers)
4
當generator完全耗盡、沒有更多值可以回傳時,它會擲出StopIteration錯誤。
>>> next(squared_numbers)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
生成器是_疊代器_的一個特殊子集。
Iterators是讓你能對_可疊代物件_進行迴圈的機制/協定。
生成器和常見 Python iterables回傳的疊代器行為非常相似,但有幾個重要差異值得注意:
生成器是_單向_的,無法「退回」先前的值。
疊代生成器會消耗回傳的值,不會重置。
生成器(由於是惰性求值)無法排序,也無法反轉。
生成器_沒有_indexes,所以你無法用加法或減法取得前一個或後一個值。
生成器不能搭配len()函式使用。
生成器可以是_有限_或_無限_的,從_無限_生成器收集所有值時要小心。
yield 運算式和return運算式非常相似。
和return運算式_不同_,yield是在_特定的時間點_把值交給呼叫方,並暫停其餘值的求值或回傳,直到它們被請求為止。
當yield被求值時,它會暫停所屬函式的執行,並回傳該函式_在那個當下_的值。
接著函式會_保持在作用域中_,而當__next__()被呼叫時,執行會繼續進行,直到再次遇到yield。
在函式之外禁止使用yield運算式。
>>> def infinite_sequence():
... current_number = 0
... while True:
... yield current_number
... current_number += 1
>>> lets_try = infinite_sequence()
>>> lets_try.__next__()
0
>>> lets_try.__next__()
1
生成器在許多應用中都很實用。
處理大型集合時,你可能不會想把它的所有值都放進memory。
生成器可以逐筆處理較大的資料,藉此節省記憶體並提升效能。
當某個流程或計算很_複雜_、很_昂貴_或_無限_時,生成器也非常有幫助:
>>> def infinite_sequence():
... current_number = 0
... while True:
... yield current_number
... current_number += 1
現在,每當對infinite_sequence物件呼叫__next__()時,它就會回傳_前一個數字_ + 1。