推導式是 Python 最具特色的語法糖之一,以宣告式風格在單行內完成「過濾 → 轉換 → 收集」三步驟,不僅程式碼簡潔,執行速度通常也快於等價的 for 迴圈 + append。本文系統整理列表、字典、集合、巢狀列表四大推導式語法、適用場景、效能比較與常見陷阱。
| 類型 | 語法 | 輸出 |
|---|---|---|
| 列表 | [expr for item in iterable if cond] | list |
| 字典 | {k_expr: v_expr for item in iterable if cond} | dict |
| 集合 | {expr for item in iterable if cond} | set |
| 生成器 | (expr for item in iterable if cond) | generator |
if cond 可省略;for 子句可多個(等價巢狀迴圈,順序與寫法一致)。
names = ['Bob', 'Tom', 'alice', 'Jerry', 'Wendy', 'Smith']
# 長度 > 3 且轉大寫
[name.upper() for name in names if len(name) > 3]
# ['ALICE', 'JERRY', 'WENDY', 'SMITH']
# x 為 0-4 偶數,y 為 0-4 奇數
[(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1]
# [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
M = [[1,2,3], [4,5,6], [7,8,9]]
# 取第 3 欄 → [3, 6, 9]
[row[2] for row in M]
# 主對角線 → [1, 5, 9]
[M[i][i] for i in range(len(M))]
# 矩陣對應元素相乘(結果攤平)
N = [[2,2,2], [3,3,3], [4,4,4]]
[M[r][c] * N[r][c] for r in range(3) for c in range(3)]
# [2, 4, 6, 12, 15, 18, 28, 32, 36]
# 保持矩陣結構
[[M[r][c] * N[r][c] for c in range(3)] for r in range(3)]
# [[2, 4, 6], [12, 15, 18], [28, 32, 36]]
people = [{'age': 42}, {'age': 45}]
# 年齡 >= 45 加 100,否則維持
[rec['age'] + 100 if rec['age'] >= 45 else rec['age'] for rec in people]
# [42, 145]
strings = ['import', 'is', 'with', 'if', 'file', 'exception']
# 字串 -> 索引
{key: val for val, key in enumerate(strings)}
# {'import': 0, 'is': 1, 'with': 2, 'if': 3, 'file': 4, 'exception': 5}
# 反轉鍵值(值必須唯一)
{v: k for k, v in {'a': 1, 'b': 2}.items()}
# {1: 'a', 2: 'b'}
strings = ['a', 'is', 'with', 'if', 'file', 'exception']
# 字串長度集合(自動去重)
{len(s) for s in strings}
# {1, 2, 4, 9}
names = [
['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']
]
# 找出含 2 個以上 'e' 的名字
# 關鍵:for 子句順序 = 巢狀迴圈順序
[name for lst in names for name in lst if name.count('e') >= 2]
# ['Jefferson', 'Wesley', 'Steven', 'Jennifer']
# 等價迴圈
result = []
for lst in names:
for name in lst:
if name.count('e') >= 2:
result.append(name)
import timeit
# 列表推導式
t1 = timeit.timeit("[x*2 for x in range(1000)]", number=10000)
# for + append
t2 = timeit.timeit("res=[]\nfor x in range(1000): res.append(x*2)", number=10000)
print(f"推導式: {t1:.3f}s, 迴圈: {t2:.3f}s, 加速比: {t2/t1:.2f}x")
# 典型結果:推導式快 1.2-1.5 倍
原因:推導式在 C 層直接分配列表記憶體並填充,避免繼續呼叫 append 方法查找。
| 陷阱 | 錯誤範例 | 正確寫法 |
|---|---|---|
| 變數洩漏(Py2/舊版) | [x for x in range(3)]; print(x) → 2 | Py3 已修復,無洩漏 |
| 可變預設值陷阱 | [[] for _ in range(3)] ✅ / [[]]*3 ❌ 共用同一列表 | 永遠用推導式建立獨立物件 |
| 過度複雜邏輯 | 單行超過 80 字元、多層巢狀 | 拆分為函數或多行推導式(括號包裹) |
| 記憶體爆炸 | 大量資料用列表推導式 | 改用生成器 (...) 惰性求值 |
result = [
transform(item)
for item in iterable
if condition(item)
]
# 或括號包裹單行邏輯
result = (transform(item)
for item in iterable
if condition(item))
推導式是 Python「簡潔而不失可讀」哲學的集中體現。掌握四大類型語法、巢狀順序、條件運算子與生成器替代策略,能讓資料處理程式碼既優雅又高效。記住:能用推導式就用,太複雜就拆函數,這是 Pythonic 風格的黃金法則。