生成器介紹
生成器表示式類似於列表,字典和集合理解,但用括號括起來。當它們用作函式呼叫的唯一引數時,不必存在括號。
expression = (x**2 for x in range(10))
此示例生成 10 個第一個完美正方形,包括 0(其中 x = 0)。
生成器函式類似於常規函式,除了它們的主體中有一個或多個 yield
語句。這樣的功能不能提供任何值(如果你想提前停止發電機,則允許空的 return
s)。
def function():
for x in range(10):
yield x**2
此生成器函式等效於前一個生成器表示式,它輸出相同。
注意 :所有生成器表示式都有自己的等效函式,但反之亦然。
如果兩個括號都重複,則可以使用不帶括號的生成器表示式:
sum(i for i in range(10) if i % 2 == 0) #Output: 20
any(x = 0 for x in foo) #Output: True or False depending on foo
type(a > b for a in foo if a % 2 == 1) #Output: <class 'generator'>
代替:
sum((i for i in range(10) if i % 2 == 0))
any((x = 0 for x in foo))
type((a > b for a in foo if a % 2 == 1))
但不是:
fooFunction(i for i in range(10) if i % 2 == 0,foo,bar)
return x = 0 for x in foo
barFunction(baz, a > b for a in foo if a % 2 == 1)
呼叫生成器函式會生成一個生成器物件,以後可以對其進行迭代。與其他型別的迭代器不同,生成器物件只能遍歷一次。
g1 = function()
print(g1) # Out: <generator object function at 0x1012e1888>
請注意,生成器的主體不會立即執行:在上面的示例中呼叫 function()
時,它會立即返回生成器物件,而不執行第一個 print 語句。這允許生成器比返回列表的函式消耗更少的記憶體,並且它允許建立生成無限長序列的生成器。
出於這個原因,生成器通常用於資料科學以及涉及大量資料的其他環境。另一個優點是其他程式碼可以立即使用生成器產生的值,而無需等待生成完整的序列。
但是,如果你需要多次使用生成器生成的值,並且生成它們的成本高於儲存成本,那麼將生成的值儲存為 list
可能比重新生成序列更好。有關詳細資訊,請參閱下面的重置發電機。
通常,生成器物件用於迴圈或任何需要迭代的函式中:
for x in g1:
print("Received", x)
# Output:
# Received 0
# Received 1
# Received 4
# Received 9
# Received 16
# Received 25
# Received 36
# Received 49
# Received 64
# Received 81
arr1 = list(g1)
# arr1 = [], because the loop above already consumed all the values.
g2 = function()
arr2 = list(g2) # arr2 = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
由於生成器物件是迭代器,因此可以使用 next()
函式手動迭代它們。這樣做將在每次後續呼叫時逐個返回產生的值。
在引擎蓋下,每次在生成器上呼叫 next()
時,Python 都會在生成器函式體中執行語句,直到它到達下一個 yield
語句。此時它返回 yield
命令的引數,並記住發生的那一點。再次呼叫 next()
將從該點恢復執行並繼續直到下一個 yield
語句。
如果 Python 到達生成器函式的末尾而不再遇到任何 yield
s,則會引發 StopIteration
異常(這是正常的,所有迭代器都以相同的方式執行)。
g3 = function()
a = next(g3) # a becomes 0
b = next(g3) # b becomes 1
c = next(g3) # c becomes 2
...
j = next(g3) # Raises StopIteration, j remains undefined
請注意,在 Python 2 中,生成器物件具有 .next()
方法,可用於手動迭代生成的值。在 Python 3 中,此方法已替換為所有迭代器的 .__next__()
標準。
重置發電機
請記住,你只能迭代生成器生成的物件一次。如果你已經在指令碼中迭代了物件,那麼任何進一步的嘗試都將產生 None
。
如果需要多次使用生成器生成的物件,可以再次定義生成器函式並再次使用它,或者,也可以在首次使用時將生成器函式的輸出儲存在列表中。如果要處理大量資料,重新定義生成器函式將是一個不錯的選擇,並且儲存所有資料項的列表將佔用大量磁碟空間。相反,如果最初生成專案的成本很高,你可能更願意將生成的專案儲存在列表中,以便你可以重複使用它們。