生成器本身是一種只能輸出數據的結構,它不像列表或其他容器可以存儲數據並操作。它是一種(lazy evaluation)輸出數據的結構,生成器僅在需要時生成數據。因此,它對於處理大數據集或無限序列時非常高效。
然而,生成器與列表或其他容器不同的一個特點是,生成器通常只能輸出數據,但在某些情況下,我們希望生成器能夠根據外部的輸入來動態生成新的結果。這就是 send() 方法的作用。
send() 方法向生成器內部傳遞數據。生成器的工作機制:
- 生成器函數的定義: 生成器函數定義與普通函數類似,但它使用
yield來返回數據。 - yield 暫停與恢復執行: 當生成器函數執行到
yield,它會暫停並返回當前值。下一次調用生成器(通過next()或send())時,會從暫停處繼續執行。 - send() 傳送數據: 除了使用
next()獲取下一個yield的值,還可以用send(value)將數據發送給生成器。在yield表達式處,生成器會接收到send()發送的值並進行處理。
示例:
def my_generator():
value = 0
while True:
received = yield value
if received is not None:
value = received
else:
value += 1
gen = my_generator()
# 獲取生成器的初始值
print(next(gen)) # Output: 0
# 透過 send() 傳遞數據給生成器
print(gen.send(10)) # Output: 10
# 不傳送數據,生成器繼續計算
print(next(gen)) # Output: 11
解釋:
next(gen):啟動生成器,返回初始值0。gen.send(10):將10傳送給生成器,接收yield,此時生成器內部變量value被設置為10。next(gen):當沒有傳送新值時,生成器默認將value加1,所以輸出為11。
常見用途:
- 協程:生成器協程允許生成器與外部代碼動態通信。
- 事件驅動編程:在多步處理過程中,動態接收和處理輸入。
- 數據流處理:生成器可以根據外部輸入即時計算,避免一次性加載所有數據。
「生成器協程」、「事件驅動編程」、「數據流處理」的 生成器簡單範例
1. 協程(生成器協程允許生成器與外部代碼動態通信)
協程是生成器的擴展,它可以讓你動態接收來自外部的數據並進行處理。
def coroutine_example():
print("協程啟動")
while True:
received = yield
print(f"接收到的數據: {received}")
# 啟動生成器協程
gen = coroutine_example()
next(gen) # 啟動協程
gen.send("第一個數據")
gen.send("第二個數據")

解釋:
coroutine_example是一個協程,它通過yield暫停,並等待send()傳送的數據。每次send()傳送後,協程內部會打印接收到的數據。
2. 事件驅動編程(在多步處理過程中,動態接收和處理輸入)
生成器可以用於事件驅動的場景,接收外部事件並根據事件狀態進行操作。
def event_driven_generator():
print('開始接收外部事件')
event_count = 0
while True:
event = yield event_count
if event == "click":
event_count += 1
elif event == "reset":
event_count = 0
print(f"事件數量: {event_count}")
gen = event_driven_generator()
next(gen) # 啟動生成器
# 模擬事件
gen.send("click")
gen.send("click")
gen.send("reset")
gen.send("click")

解釋:
event_driven_generator生成器接收不同的事件,例如「click」或「reset」。每次收到「click」事件時,會遞增計數,收到「reset」時重置計數。
3. 數據流處理(生成器可以根據外部輸入即時計算,避免一次性加載所有數據)
這個範例展示了如何利用生成器進行數據流處理,動態生成數據而不是一次性加載。
def data_stream_processor(data):
print(f'接收資料')
total = 0
for item in data:
total += item
print(f'處理第{item}個資訊 :{total}')
processed_value = yield total
if processed_value is not None:
total = processed_value
print(f'處理接收的資訊 :{total}')
gen = data_stream_processor([1, 2, 3, 4, 5])
print(next(gen)) # 處理第一個數據
print(gen.send(10)) # 更新總和為 10 並繼續
print(next(gen)) # 繼續處理
print(next(gen)) # 繼續處理
print(gen.send(10)) # 更新總和為 10 並繼續

解釋:
- 接收資料:當你調用這個生成器時,它會接收一個
data列表作為輸入。 total:這是一個累加變數,初始值為 0,用來計算資料的總和。for item in data:生成器逐步處理data列表中的每個項目,每次迭代會將當前的項目item加到total中。yield total:這行代碼會返回目前的total值,同時生成器會暫停並等待下一個操作(通過next()或send())。processed_value = yield total:這允許外部使用send()方法傳入數據。傳入的數據會賦值給processed_value。如果processed_value不是None,則生成器會更新total的值,這樣可以動態調整累加的結果。
執行步驟
gen = data_stream_processor([1, 2, 3, 4, 5])
這行程式碼會創建生成器對象 gen,並將列表 [1, 2, 3, 4, 5] 傳遞給它。
print(next(gen)) # 處理第一個數據
next(gen)會啟動生成器的執行,並讓它跑到第一個yield位置。- 它會處理列表中的第一個項目
1,計算總和total = 1,並暫停在yield。 - 輸出:
接收資料
處理第1個資訊 :1
1
print(gen.send(10)) # 更新總和為 10 並繼續
- 這裡調用
send(10),傳入的數值10會作為processed_value,並影響生成器的執行。 - 生成器接收到
processed_value = 10,所以total會更新為10,然後繼續處理下一個數據(第 2 項2)。 - 總和變為
total = 10 + 2 = 12,並暫停在yield。 - 輸出:
處理接收的資訊 :10
處理第2個資訊 :12
12
print(next(gen)) # 繼續處理
- 再次使用
next(gen),生成器會從暫停處繼續執行,處理第三個數據3,總和更新為total = 12 + 3 = 15。 - 輸出:
處理第3個資訊 :15
15
print(next(gen)) # 繼續處理
- 生成器繼續處理第四個數據
4,總和變為total = 15 + 4 = 19。 - 輸出:
處理第4個資訊 :19
19
print(gen.send(10)) # 更新總和為 10 並繼續
- 最後,使用
send(10)再次將total更新為10,然後處理最後一個數據5,最終總和變為total = 10 + 5 = 15。 - 輸出:
處理接收的資訊 :10
處理第5個資訊 :15
15
總結:
- 生成器
data_stream_processor逐步處理列表中的數據,並且允許通過send()動態修改內部狀態(即total)。 - 每次
yield都會暫停生成器的執行,等待外部的操作(next()或send()),並根據外部數據更新狀態。
總結:
- 生成器
data_stream_processor逐步處理列表中的數據,並且允許通過send()動態修改內部狀態(即total)。 - 每次
yield都會暫停生成器的執行,等待外部的操作(next()或send()),並根據外部數據更新狀態。













