本文 AI 產出,尚未審核
Python – 函式(Functions)
主題:Lambda 匿名函式
簡介
在日常的 Python 程式開發中,函式是組織程式碼、提升可讀性與重用性的基礎工具。除了傳統的 def 方式外,Python 也提供了 lambda(匿名函式)語法,讓開發者可以在單行內快速建立簡短的函式物件。
Lambda 的出現主要是為了配合 函式式編程(functional programming)風格,例如 map()、filter()、sorted() 等內建高階函式。掌握 Lambda 不僅能讓程式碼更精簡,還能在 事件處理、資料轉換、即時回呼 等情境中大幅提升開發效率。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶領讀者了解如何在 Python 中安全、有效地使用 Lambda。
核心概念
1. Lambda 的基本語法
lambda 參數列表: 表達式
- 參數列表 可以是零個或多個,允許使用預設值。
- 表達式 必須是單一的 Python 表達式,不能包含敘述(statement)。
# 無參數的 Lambda,回傳固定文字
greet = lambda: "Hello, Python!"
print(greet()) # → Hello, Python!
# 單一參數,回傳平方值
square = lambda x: x * x
print(square(5)) # → 25
註:Lambda 產生的是 函式物件,可以像普通函式一樣被呼叫、傳遞或存入變數。
2. 為什麼要使用 Lambda?
| 情境 | 使用 def 的寫法 |
使用 Lambda 的寫法 |
|---|---|---|
map 轉換列表 |
python<br>def double(x):<br> return x * 2<br>list(map(double, nums))<br> |
python<br>list(map(lambda x: x * 2, nums))<br> |
sorted 的排序鍵 |
python<br>def by_len(s):<br> return len(s)<br>sorted(words, key=by_len)<br> |
python<br>sorted(words, key=lambda s: len(s))<br> |
| 事件回呼(GUI) | python<br>def on_click(event):<br> print("Clicked!")<br>button.bind("<Button-1>", on_click)<br> |
python<br>button.bind("<Button-1>", lambda e: print("Clicked!"))<br> |
從表中可見,Lambda 能讓 一行即完成 的需求更直觀,減少額外的命名空間與程式碼雜訊。
3. Lambda 與 map()、filter()、reduce()
numbers = [1, 2, 3, 4, 5]
# 1. map:將每個元素乘以 10
times_ten = list(map(lambda x: x * 10, numbers))
print(times_ten) # → [10, 20, 30, 40, 50]
# 2. filter:挑選出偶數
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # → [2, 4]
# 3. reduce(需要 from functools import reduce)
from functools import reduce
product = reduce(lambda a, b: a * b, numbers)
print(product) # → 120
重點:
map、filter、reduce都接受 可呼叫物件,Lambda 正好符合此需求,讓資料流的轉換一步到位。
4. Lambda 作為 sorted()、list.sort() 的 key
people = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Carol", "age": 27},
]
# 依年齡遞增排序
people_sorted = sorted(people, key=lambda p: p["age"])
print(people_sorted)
# → [{'name': 'Bob', 'age': 25}, {'name': 'Carol', 'age': 27}, {'name': 'Alice', 'age': 30}]
如果排序條件較為複雜,也可以把多個條件合併:
# 先依年齡遞增,年齡相同再依名字字母順序
people_sorted = sorted(people, key=lambda p: (p["age"], p["name"]))
5. 捕獲外部變數(閉包)
Lambda 會 閉包(closure)捕獲其所在作用域的變數。需要注意的是,捕獲的是 變數本身,而非當下的值,這在迴圈中常會導致意外行為。
funcs = []
for i in range(5):
funcs.append(lambda: i) # 捕獲的是 i 本身
print([f() for f in funcs]) # → [4, 4, 4, 4, 4](全部是最後的 i)
# 正確寫法:使用預設參數把當下值「凍結」住
funcs = []
for i in range(5):
funcs.append(lambda i=i: i)
print([f() for f in funcs]) # → [0, 1, 2, 3, 4]
常見陷阱與最佳實踐
1. 可讀性
- Lambda 應僅用於 簡單、單行 的運算。若邏輯超過兩行,建議改用
def,避免程式碼難以維護。 - 為了可讀性,盡量 避免在同一行寫太多,例如
lambda x, y: (x+y, x-y)這類返回多個值的寫法,應該拆成具名函式。
2. 除錯困難
- Lambda 沒有名稱,堆疊追蹤時會顯示
<lambda>,不易定位錯誤。 - 若需要在除錯階段觀察中間結果,請暫時改寫成具名函式或使用
functools.partial。
3. 效能考量
- 在大量迭代的情境下,
def定義的函式與 Lambda 的效能差異可忽略不計。 - 但若在 熱路徑(hot path)中頻繁建立 Lambda(例如在迴圈內每次都
lambda x: ...),會產生不必要的物件建立成本,建議改為一次性建立後重複使用。
4. 預設參數與閉包
- 如前所示,迴圈內使用 Lambda 時務必利用 預設參數(
lambda x=i: x)凍結當前值,避免「最後一次」的變數被所有 Lambda 共享。
5. 型別註記(Python 3.5+)
- 雖然 Lambda 本身不支援完整的函式說明文件(docstring),但可使用 型別註記 提升 IDE 輔助與可讀性:
add = lambda a: int, b: int: a + b # ← 這樣的寫法其實不合法
# 正確做法是先用變數標註,再賦值
from typing import Callable
add: Callable[[int, int], int] = lambda a, b: a + b
實際應用場景
| 場景 | 為什麼適合使用 Lambda |
|---|---|
資料清理與轉換(例如 Pandas apply) |
df['col'].apply(lambda x: x.strip().lower()) 讓每筆資料的處理保持在一行內,易於閱讀。 |
| GUI 事件回呼(Tkinter、PyQt) | button.clicked.connect(lambda: print("Clicked!")) 可直接在 UI 建構程式碼中嵌入簡短回呼,避免額外的函式宣告。 |
| 排序與搜尋 | sorted(items, key=lambda i: (i.priority, i.timestamp)) 能在排序時一次表達多層次條件。 |
| 函式式管線(pipeline) | result = reduce(lambda acc, x: acc + x, map(lambda y: y*2, data)) 把多個功能串起來,形成資料流。 |
| 測試 Stub / Mock | 在單元測試中使用 lambda 快速建立簡易的回傳函式,免除寫完整的測試替身類別。 |
總結
Lambda 是 Python 中 輕量級、即時 的函式表達方式,特別適合以下情境:
- 單行、簡單的運算(如
map、filter、sorted的 key)。 - 臨時回呼(GUI 事件、測試 stub)。
- 資料管線(結合
map、filter、reduce形成流式處理)。
在使用時,務必遵守 可讀性優先 的原則:
- 只在「簡單」的情況下使用 Lambda;
- 複雜邏輯請改用具名函式;
- 注意閉包與預設參數的行為,避免迴圈陷阱;
- 需要除錯或型別提示時,可考慮先寫具名函式再改寫成 Lambda。
掌握 Lambda 後,你將能在 Python 程式碼中寫出更 精簡、具表達力 的函式式風格,同時保持程式的可維護性與效能。祝你在實務開發中玩得開心、寫得順手!