本文 AI 產出,尚未審核
Python 課程 – 函式式編程:深入瞭解 lambda 函式
簡介
在 Python 中,函式式編程(Functional Programming) 為我們提供了一種宣告式、簡潔且可組合的程式設計風格。相較於傳統的命令式程式,函式式編程強調「資料不變」與「純函式」的概念,讓程式碼更易於測試、重構與平行化。
在這個單元,我們聚焦 lambda 函式——一種「匿名」的小型函式。它不需要使用 def 來命名,常與 map、filter、sorted、reduce 等高階函式搭配使用。掌握 lambda 後,你可以在資料處理、事件回呼、一次性運算等情境中寫出更精簡、更具可讀性的程式。
以下內容將從概念說明、實作範例、常見坑洞,到最佳實踐與實務應用,逐步帶領讀者從 初學者 進階到 中級開發者 的水平。
核心概念
1. lambda 的語法與特性
lambda 參數列表: 表達式
- 匿名:不需要
def產生函式名稱。 - 單行表達式:只能寫一個表達式,不能放多行敘述或
return。 - 即時產生:通常直接作為參數傳遞給其他函式,或賦值給變數以供稍後使用。
範例:最簡單的加法 lambda
add = lambda x, y: x + y print(add(3, 5)) # 8
2. 為什麼要使用 lambda?
| 場景 | 使用 lambda 的好處 |
|---|---|
| 一次性運算 | 不必額外定義命名函式,減少程式碼量。 |
| 高階函式 | 與 map、filter、sorted 等搭配,可在一行內完成資料轉換或排序條件。 |
| 回呼函式 | GUI、網路框架常需要傳入簡短的處理函式,lambda 可保持程式結構的緊湊。 |
3. 常見的 lambda 應用範例
3.1 map + lambda:批量轉換
numbers = [1, 2, 3, 4, 5]
# 把每個數字平方
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]
說明:
map會把lambda x: x ** 2套用到numbers的每個元素,回傳一個 iterator,使用list()轉成清單方便觀察。
3.2 filter + lambda:條件篩選
words = ["apple", "banana", "cherry", "date", "elderberry"]
# 只保留長度大於 5 的字串
long_words = list(filter(lambda w: len(w) > 5, words))
print(long_words) # ['banana', 'cherry', 'elderberry']
說明:
filter會把符合lambda w: len(w) > 5條件的元素留下。
3.3 sorted + lambda:自訂排序鍵
students = [
{"name": "Alice", "score": 88},
{"name": "Bob", "score": 95},
{"name": "Cathy", "score": 78},
]
# 依成績由高到低排序
sorted_students = sorted(students, key=lambda s: s["score"], reverse=True)
print(sorted_students)
# [{'name': 'Bob', 'score': 95}, {'name': 'Alice', 'score': 88}, {'name': 'Cathy', 'score': 78}]
說明:
key參數接受一個函式,lambda s: s["score"]會回傳每筆資料的分數作為排序依據。
3.4 reduce(functools.reduce)+ lambda:累積運算
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# 計算所有元素的乘積
product = reduce(lambda a, b: a * b, numbers)
print(product) # 120
說明:
reduce會把序列兩兩相乘,最終得到累積結果。這裡的 lambda 只做單純的二元運算。
3.5 事件回呼:GUI 按鈕的即時處理
import tkinter as tk
root = tk.Tk()
root.title("Lambda Demo")
counter = 0
def make_button():
global counter
btn = tk.Button(root, text=f"點我 {counter}",
command=lambda: on_click(counter))
btn.pack(padx=10, pady=5)
counter += 1
def on_click(idx):
print(f"按下第 {idx} 個按鈕!")
for _ in range(3):
make_button()
root.mainloop()
說明:每個按鈕的
command參數接收一個 lambda,將當前的counter值「閉包」進去,使得每個按鈕都有自己的識別編號。
常見陷阱與最佳實踐
| 陷阱 | 風險 | 解決方案 |
|---|---|---|
| 過度嵌套 | 多層 lambda 會讓程式碼難以閱讀。 | 只在簡單、一次性場景使用;複雜邏輯請改寫成具名函式。 |
| 副作用 | lambda 仍可執行有副作用的語句(例如修改全域變數),破壞「純函式」概念。 | 儘量保持 無副作用,只返回計算結果。 |
| 可讀性 | 使用過長的表達式會降低可讀性。 | 把長表達式拆成多行或改用 def;使用有意義的變數名稱。 |
| 捕捉迴圈變數 | 在迴圈裡建立 lambda 時,容易產生「閉包捕捉最後一次值」的問題。 | 使用 default argument 或 functools.partial 來固定當前值。 |
| 除錯困難 | lambda 沒有名稱,Traceback 只能顯示 <lambda>,不易定位錯誤。 |
若需要除錯,先將 lambda 抽成具名函式,或在 lambda 前加入 # noqa 註解說明。 |
最佳實踐小結
- 保持單行、單一任務:lambda 應只做一件事,且寫在一行內。
- 適度使用:在
map、filter、sorted等高階函式中使用最為自然,避免在主流程中過度堆砌。 - 命名重要:若 lambda 需要在多處被呼叫,請改寫為具名函式,以提升可維護性。
- 善用
functools:partial、wraps等工具可以讓 lambda 更靈活且不失可讀性。 - 測試與文件:即使是簡短的 lambda,也建議寫單元測試,並在程式碼旁加入說明註解。
實際應用場景
資料清理與轉換
- 在 ETL(Extract‑Transform‑Load)流程中,使用
map(lambda x: x.strip().lower(), raw_strings)快速清理字串。
- 在 ETL(Extract‑Transform‑Load)流程中,使用
API 回傳結果的排序
- 從外部 API 取得商品清單後,使用
sorted(data, key=lambda d: d["price"])即可依價格排序。
- 從外部 API 取得商品清單後,使用
即時事件處理
- 在 Web 框架(如 Flask)裡,使用
app.route('/ping')(lambda: 'pong')直接回傳固定字串。
- 在 Web 框架(如 Flask)裡,使用
機器學習特徵工程
- 用
map(lambda row: row[:2] + [row[2] * 100], dataset)在特徵向量中即時調整尺度。
- 用
多執行緒/多進程的映射
concurrent.futures.ThreadPoolExecutor().map(lambda x: heavy_compute(x), items),將 lambda 作為輕量的任務描述傳遞。
總結
- lambda 是 Python 中的匿名函式,提供 快速、簡潔 的寫法,特別適合與高階函式(
map、filter、sorted、reduce)結合。 - 正確使用 lambda 可以 減少樣板程式碼、提升 可讀性,但過度或不恰當的使用會帶來 維護負擔。
- 透過本文的概念說明、實作範例、陷阱辨識與最佳實踐,你應該已能在日常開發中自如地運用 lambda,從 資料處理、事件回呼 到 演算法實作 都能受益。
持續練習、善用測試與文件,讓 lambda 成為你函式式編程工具箱中可靠且高效的一員吧! 🚀