Python yield:生成器的威力與實戰應用

 



1. yield 就是「可中斷的 return」

直覺上,yield 看起來很像 return:遇到它就「回傳」一個值;不同的是,函式不會結束,而是暫停在此,保留當前所有區域變數與執行位置,等到下一次呼叫才會從暫停處繼續執行。
簡單來說:


def foo(): print("開始執行") while True: yield 4 # 暫停並回傳 4 print("★ 繼續執行 ★")
  • 第一次執行 g = foo() 不會執行函式內容,而只回傳一個生成器物件。

  • 呼叫 next(g),才會印出 "開始執行",並暫停在 yield 4 回傳 4

  • 再呼叫一次 next(g),將從 yield 之後的位置繼續執行,印出 ★ 繼續執行 ★,然後再次遇到 yield 4 暫停並回傳。 


2. next() 如何推動生成器

生成器物件除了可以被 next() 觸發,也可搭配迴圈自動驅動:


g = foo() print(next(g)) # → 4 print(next(g)) # → ★ 繼續執行 ★ 4 for v in foo(): print(v) # 不停印出 4,直到你手動 break
  • next(g) 等同於「進到下一個 yield」,每次都從函式暫停處繼續執行。

  • for 迴圈時,Python 會自動呼叫 next() 直到生成器遇到 return 或拋出 StopIteration。 


3. 用 send() 向生成器傳入資料

除了單純取得值,生成器還能接收外部傳入的資料──使用 send()


def foo2(): print("啟動") while True: received = yield "請傳入值" print("收到的值:", received) g2 = foo2() print(next(g2)) # 啟動,回傳 "請傳入值" print(g2.send(123)) # 收到的值:123,繼續到下一個 yield 回傳 "請傳入值"
  • 第一次用 next() 啟動生成器到第一個 yield

  • 之後用 g2.send(x),會把 x 賦給上次 yield 暫停時的接收變數,然後繼續往下執行直到下一個 yield。 


4. 何以用生成器能省記憶體?

若你要一次性產生一個長度為 1,000,000 的列表,range(1000000)(在 Python 3 中雖為生成器,但概念相同)就能按需產生值,而不需先佔用整塊記憶體。


# 傳統寫法:一次性建立完整清單 nums = [i for i in range(1000000)] # 佔用大量記憶體 # 生成器寫法:按需產生 def gen_nums(n): for i in range(n): yield i for x in gen_nums(1000000): # 處理 x pass
  • 生成器只有在 next() 時才「跑」到下一個值,永遠只保留當下狀態,不會將所有元素同時載入記憶體。

  • 對於「流式處理」(stream processing)、讀取大檔案或網路資料,都能大幅減少記憶體用量。 


5. 常見應用場景與建議

  1. 大資料流處理:如逐行讀取大型檔案

  2. 無限序列:如自訂迭代器產生無窮數列

  3. 協程(Coroutine):透過 send()yield 實作更複雜的流程控制

  4. 管道串接:將多個生成器串成資料處理管線

實作小提示:

  • 在生成器中避免過度深層邏輯,保持單一職責,並妥善捕捉 StopIteration

  • 若想取得「最後一次 return」的值,可在函式末尾加上 return value,並在外層捕捉 StopIteration.value


結語

yield 的核心魔力,在於「暫停並記憶狀態」,讓函式化身生成器,邊執行邊產出,對大資料與流式處理場景尤為適合。掌握好 next()send() 之後,你就能寫出兼具高效與優雅的 Python 程式。歡迎在評論區分享你的 yield 應用範例!

留言

這個網誌中的熱門文章

windows server 「虛擬化型保護的程式碼完整性」(Virtualization Based Protection of Code Integrity,VBS) 功能說明與設定

linux server - 檢查nvidia driver 異常並自動重新開機

Docker 應用: 再以建置好container狀況下 如何透過json設定檔設定 在修改 Working Directory 和 Restart Policy 設定