本文 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

重點mapfilterreduce 都接受 可呼叫物件,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 中 輕量級、即時 的函式表達方式,特別適合以下情境:

  1. 單行、簡單的運算(如 mapfiltersorted 的 key)。
  2. 臨時回呼(GUI 事件、測試 stub)。
  3. 資料管線(結合 mapfilterreduce 形成流式處理)。

在使用時,務必遵守 可讀性優先 的原則:

  • 只在「簡單」的情況下使用 Lambda;
  • 複雜邏輯請改用具名函式;
  • 注意閉包與預設參數的行為,避免迴圈陷阱;
  • 需要除錯或型別提示時,可考慮先寫具名函式再改寫成 Lambda。

掌握 Lambda 後,你將能在 Python 程式碼中寫出更 精簡、具表達力 的函式式風格,同時保持程式的可維護性與效能。祝你在實務開發中玩得開心、寫得順手!