Python yield:生成器的威力與實戰應用
1. yield
就是「可中斷的 return」
直覺上,yield
看起來很像 return
:遇到它就「回傳」一個值;不同的是,函式不會結束,而是暫停在此,保留當前所有區域變數與執行位置,等到下一次呼叫才會從暫停處繼續執行。
簡單來說:
-
第一次執行
g = foo()
不會執行函式內容,而只回傳一個生成器物件。 -
呼叫
next(g)
,才會印出"開始執行"
,並暫停在yield 4
回傳4
。 -
再呼叫一次
next(g)
,將從yield
之後的位置繼續執行,印出★ 繼續執行 ★
,然後再次遇到yield 4
暫停並回傳。
2. next()
如何推動生成器
生成器物件除了可以被 next()
觸發,也可搭配迴圈自動驅動:
-
next(g)
等同於「進到下一個yield
」,每次都從函式暫停處繼續執行。 -
用
for
迴圈時,Python 會自動呼叫next()
直到生成器遇到return
或拋出StopIteration
。
3. 用 send()
向生成器傳入資料
除了單純取得值,生成器還能接收外部傳入的資料──使用 send()
:
-
第一次用
next()
啟動生成器到第一個yield
。 -
之後用
g2.send(x)
,會把x
賦給上次yield
暫停時的接收變數,然後繼續往下執行直到下一個yield
。
4. 何以用生成器能省記憶體?
若你要一次性產生一個長度為 1,000,000 的列表,range(1000000)
(在 Python 3 中雖為生成器,但概念相同)就能按需產生值,而不需先佔用整塊記憶體。
-
生成器只有在
next()
時才「跑」到下一個值,永遠只保留當下狀態,不會將所有元素同時載入記憶體。 -
對於「流式處理」(stream processing)、讀取大檔案或網路資料,都能大幅減少記憶體用量。
5. 常見應用場景與建議
-
大資料流處理:如逐行讀取大型檔案
-
無限序列:如自訂迭代器產生無窮數列
-
協程(Coroutine):透過
send()
與yield
實作更複雜的流程控制 -
管道串接:將多個生成器串成資料處理管線
實作小提示:
-
在生成器中避免過度深層邏輯,保持單一職責,並妥善捕捉
StopIteration
。 -
若想取得「最後一次 return」的值,可在函式末尾加上
return value
,並在外層捕捉StopIteration.value
。
結語
yield
的核心魔力,在於「暫停並記憶狀態」,讓函式化身生成器,邊執行邊產出,對大資料與流式處理場景尤為適合。掌握好 next()
與 send()
之後,你就能寫出兼具高效與優雅的 Python 程式。歡迎在評論區分享你的 yield
應用範例!
留言
張貼留言