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 應用範例!
留言
張貼留言