這篇文章將帶你從最基礎的 `yield` 開始,一路講到 `yield from` 的應用場景,並解釋為什麼在處理巢狀結構(如多層列表、樹狀目錄、或是 Word 表格)時,你是如此需要它。
—
## 1. 基礎篇:什麼是 `yield`?
在 Python 中,如果一個函式使用了 `return`,它執行完畢就會「銷毀」並回傳結果。但如果使用了 `yield`,這個函式就變成了一個 **生成器 (Generator)**。
**生成器的特性:**
1. **暫停與繼續**:程式執行到 `yield` 時會「暫停」,把值交出去。
2. **省記憶體**:它不是一次把所有東西存成 List,而是「要一個、做一個」(Lazy Evaluation)。
### 範例:做麵包
假設你要做 3 個麵包。
**傳統方式 (return List)**:
先把 3 個麵包全做完,放在一個大籃子裡,一次端給客人。
def make_bread_list():
breads = []
breads.append("麵包 1")
breads.append("麵包 2")
breads.append("麵包 3")
return breads # 一次全部給
for b in make_bread_list():
print(f"客人吃掉了: {b}")
**生成器方式 (yield)**:
做完第 1 個,馬上端出去給客人吃(函式暫停),客人吃完回來要第 2 個,你再繼續做。
# %%
def make_bread_gen():
yield "麵包 1" # 端出去,暫停
yield "麵包 2" # 端出去,暫停
yield "麵包 3" # 端出去,結束
for b in make_bread_gen():
print(f"客人吃掉了: {b}")
## 2. 進階篇:為什麼需要 `yield from`?
當你的生成器邏輯變得複雜,需要**呼叫另一個生成器**幫忙時,問題就來了。
### 情境:外包工廠
假設你是一個總召 (Main Generator),你負責產出產品。但中間有一部分產品,你外包給「分包商 (Sub Generator)」去做。
#### ❌ 舊寫法 (沒有 yield from)
你需要寫一個迴圈,從分包商那裡一個個拿貨,再一個個交給客戶。
def sub_task():
yield "零件 A"
yield "零件 B"
def main_task():
yield "主產品 Start"
# 這裡很麻煩:你需要手動跑迴圈去接分包商的貨
for item in sub_task():
yield item
yield "主產品 End"
# 測試
for i in main_task():
print(i)
**缺點**:如果分包商還有分包商(多層巢狀),你的程式碼充滿了這種轉手的 `for` 迴圈。
#### ✅ 新寫法 (使用 yield from)
`yield from` 就像建立了一條**直通管道**。你告訴 Python:「接下來的時間交給 `sub_task`,它吐出什麼,直接轉傳給最原本的呼叫者,不用經過我的手。」
def sub_task():
yield "零件 A"
yield "零件 B"
def main_task():
yield "主產品 Start"
# 超簡潔!直接把控制權「委派」給子生成器
yield from sub_task()
yield "主產品 End"
# 測試結果一模一樣,但程式碼更乾淨
for i in main_task():
print(i)
## 3. `yield from` 的強大之處:處理樹狀結構
這就是為什麼在 `docx` 案例中,`yield from` 這麼好用。
當結構是 **”樹狀” (Tree)** 或 **”巢狀” (Nested)** 時,
遞迴 + `yield from` 是絕配。
### 範例:拆解多層紙箱
假設我們有很多紙箱,紙箱裡可能有球,
也可能還有小紙箱。我們要一次列出所有的球。
data = [
"球 1",
[
"球 2",
"球 3",
["球 4", "球 5"] # 紙箱裡的紙箱
],
"球 6"
]
def get_all_balls(box):
for item in box:
if isinstance(item, list):
# 遇到紙箱(list),就委派給自己(遞迴)去處理那個紙箱
# 這裡把內層找到的球,直接傳到最外層
yield from get_all_balls(item)
else:
# 遇到球,直接交出去
yield item
print(list(get_all_balls(data)))
# 輸出: ['球 1', '球 2', '球 3', '球 4', '球 5', '球 6']
這就是 `yield from` 的精髓:**委派 (Delegation)** 與 **扁平化 (Flattening)**。
推薦hahow線上學習python: https://igrape.net/30afN










近期留言