生成器介紹

生成器表示式類似於列表,字典和集合理解,但用括號括起來。當它們用作函式呼叫的唯一引數時,不必存在括號。

expression = (x**2 for x in range(10))

此示例生成 10 個第一個完美正方形,包括 0(其中 x = 0)。

生成器函式類似於常規函式,除了它們的主體中有一個或多個 yield 語句。這樣的功能不能提供任何值(如果你想提前停止發電機,則允許空的 returns)。

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 到達生成器函式的末尾而不再遇到任何 yields,則會引發 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

如果需要多次使用生成器生成的物件,可以再次定義生成器函式並再次使用它,或者,也可以在首次使用時將生成器函式的輸出儲存在列表中。如果要處理大量資料,重新定義生成器函式將是一個不錯的選擇,並且儲存所有資料項的列表將佔用大量磁碟空間。相反,如果最初生成專案的成本很高,你可能更願意將生成的專案儲存在列表中,以便你可以重複使用它們。