生成器本身是一種只能輸出數據的結構,它不像列表或其他容器可以存儲數據並操作。它是一種(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()
),並根據外部數據更新狀態。