f = lambda x: (lambda y: x + y)
print(f(2)(3)) # 55
Anthony
2026年5月10日
撰寫 Python 時,或多或少有過這種經驗:明明只想把一串資料「改一改、篩一篩、加總一下」,結果迴圈越寫越長,if 越疊越深,最後自己都不想讀。這時候,lambda、map、filter、reduce 通常會被拿出來,像是 Python 世界裡的瑞士刀。
多數教學會告訴你它們可以把程式碼變短。此言固然不假,卻仍偏於皮相;若見木不見林,便容易錯過其背後真正的設計脈絡。這四個工具其實對應了清楚的數學與演算法思維,因此這篇文章不只會教你怎麼寫,更會回答「為什麼這樣寫」。
lambda:匿名函式很多人第一次覺得 lambda 好用,不是在考試題,而是在工作現場。例如 PM 說:
這份商品清單幫我按折扣後價格排序,最高的排前面。
當然可以先寫一個 def,再塞進 sorted。問題是,這段邏輯可能只會用一次,卻讓程式結構變得很重。lambda 的價值就在這裡:把一次性、小範圍、可一眼看懂的邏輯直接放回資料流程本身。
在進入 Python 語法前,得先了解一下 lambda 的數學源頭。lambda 這個名稱來自\(\lambda\)-演算(Lambda Calculus):一套以函式為核心的形式系統。它最基本的元素只有三種:
xλx.M(把 x 綁定在表達式 M)(M N)(把函式 M 套用到輸入 N)最常見的規則叫 \(\beta\)-化簡(beta reduction):
\[ ((\lambda x.(x+1))(2)) \;\underset{\beta}{\mapsto}\; (2+1) \;\underset{\text{eval}}{\mapsto}\; 3 \]
也就是把 x 用 2 代入函式本體。再看一個雙參數例子:
\[ (((\lambda x.(\lambda y.(x+y)))(2))(3)) \;\underset{\beta}{\mapsto}\; ((\lambda y.(2+y))(3)) \;\underset{\beta}{\mapsto}\; (2+3) \;\underset{\text{eval}}{\mapsto}\; 5 \]
這個概念在 Python 中也能直觀看到:
lambda 是什麼講了那麼多數學,還是回到 Python——lambda 究竟是什麼?事實上,lambda 就是一種匿名函式(anonymous function)2寫法,適合把短小邏輯直接放在管線中。其基本語法為:
和 def 的關鍵差異:
lambda 是運算式,可以直接放在函式參數裡lambda 只能寫單一運算式,不能寫多行敘述def 與 lambda 對照6
6
若要更好理解,可以把 def 是為一個正式工具箱,lambda 則是一把口袋小刀。兩者都能切東西,但場景不同。需要重複使用、命名清晰、好維護時,def 還是首選。
lambda 實戰案例假設有以下資料:
若要按價格排序:
[{'name': 'Monitor', 'price': 4990, 'sales': 120}, {'name': 'Keyboard', 'price': 1290, 'sales': 220}, {'name': 'Mouse', 'price': 790, 'sales': 430}]
若改為按預估營收(price * sales)排序:
[{'name': 'Monitor', 'price': 4990, 'sales': 120}, {'name': 'Mouse', 'price': 790, 'sales': 430}, {'name': 'Keyboard', 'price': 1290, 'sales': 220}]
這就是 lambda 最強而有力的地方,可以讓閱讀或維護程式碼的人一看就懂。
lambda 使用時機如果出現了「把很多條件硬塞進一行」的想法在腦海中出現,通常就是該停下來的時候。例如以下的例子:
f 這個函數寫得又臭又長,且邏輯過度壓縮,可讀性很差。改成 def 可讀性會好很多:
因此 lambda 與 def 的使用時機需視情況而定:
lambdadef程式設計不是比誰寫得最短,而是比誰在若干個月之後還看得懂自己在寫些什麼。
map:轉換函式lambda 解決的是小函式要放哪裡,而 map 解決的是同一個轉換要怎麼套到整批資料。在資料清理、特徵工程或報表前處理中,可能會一直遇到這種需求:每一筆都做相同動作,例如去空白、轉型別、單位換算、格式統一…。
如果每次都手寫 for 迴圈,當然可以完成任務;但 map 的好處是語意更直接:直接宣告轉換規則而非控制流程。
根據 Python 官方文件,map 的基本語法為:
可以這樣理解:
function:為要套用的函式iterable:第一個可迭代物件*iterables:可傳多個可迭代物件,會平行取值套入 functionstrict=False:預設為 False;若設為 True,多個可迭代物件的長度不一致時會拋出 ValueError另外補充:strict 是 Python 3.14 新增參數,若使用舊版本則沒有這個參數,純屬正常現象。
map 實戰案例假設有以下的列表:
沒有學過 map 但有點 Python 基礎的的人,可能會用列表推導式(list comprehension)這樣寫:
[1, 4, 9, 16, 25]
不過既然列表是一個可迭代物件,就應該善用此性質,將其丟入 map 中:
這段其實就是一句話:把 x ** 2 這條規則,套用到 nums 裡的每個 x。如此寫法寫起來更加簡潔有力,也毋需額外定義一個可能只會用一次的函數。
map 常見陷阱執得注意的是,map 在 Python 3 中不是直接回傳列表,而是一個迭代器,代表它通常會被消耗:
[10, 20, 30]
[]
第二次變空不是 bug,而是第一次已經走完。如果結果要重複使用,則需要先轉成列表後存下來。
map 使用時機當遇到以下情況時,建議使用 map:
filter:篩選函式真實資料常常不乾淨:空字串、缺漏值、測試資料混在正式資料裡。很多時候在做資料處理時的第一步不是思考如何算得更快,而是先決定哪些資料值得留下作後續分析。
根據 Python 官方文件,filter 的語法是:
當我們將一個函式丟進 filter 後:
function 回傳 True 的元素會被保留False 的元素會被丟棄filter 回傳的是迭代器filter 實戰案例假設有以下成績資料:
如果用傳統寫法,可能會這樣寫:
[95, 71, 82, 64]
問題仍舊與之前一樣:又多了一個可能只會用一次的函式。將 scores 丟進 filter 後,使用 lambda 加入篩選條件,就可以變得更加簡潔:
filter 小技巧在篩選資料時,最常遇到也最需要的是將一些不必要、不需要的值剔除,以確保資料的乾淨。而 filter 有個特別的地方:若 function 傳 None,filter 會保留所有為真的元素:
['Alice', 'Bob', 'Cathy']
雖然這在清理輸入資料時很常用,但也要注意:0 與 False 也會被濾掉,使用時必須謹慎。
filter 常見陷阱和 map 一樣,filter 也是回傳一個迭代器,因此通常只能被消耗一次:
[2, 4]
[]
如果後面還要重複使用,請先轉成列表存起來。
reduce:收斂函式很多任務最後都要變成單一結果,例如總和、連乘、最大值、加權分數,或一份摘要字典。這些都屬於 reduce 的典型場景。reduce 的核心概念很簡單:維護一個累積值(accumulator),每讀到一筆新資料,就更新一次累積值,直到資料走完。
注意到,reduce 函式是在 functools 裡,而不是內建函式。根據 Python 官方文件,reduce 的語法為:
function(acc, x):把累積值 acc 和目前元素 x 合併後回傳新累積值iterable:要收斂的無間initial:初始值(建議明確給定)reduce 實戰案例假設有以下列表:
當要加總列表中所有元素時,直觀上來說會寫一個 for 迴圈,最後回傳加總值:
但眾所週知,迴圈效率不高,能不使用迴圈就儘量避免。透過 reduce 函式搭配 lambda,可以將上述迴圈轉換為以下寫法:
這段就等價於以下運算:
((((0 + 1) + 2) + 3) + 4) + 5
同理,如果要連乘,寫法也很簡單:
initial 參數的重要性前面提到 initial 參數雖然可以不給定,但有時若不給 initial,碰到空資料就會噴錯。例如:
reduce() of empty iterable with no initial value
因此建議在多數情境都明確提供 reduce 一個初始值。
filter + map + reduce既然已有如此多利器,那麼就來一次大彙總。假設有以下的列表:
我們要求將列表中的元素保留正數,再將每個元素平方,最後加總,則我們可以用以下的寫法完成上述任務:
45
再給一個更貼近實際資料分析的情境,想像拿到一份原始訂單明細,裡面會混有測試單、退貨單、缺漏欄位與錯誤型別。
orders = [
{"order_id": "A001", "status": "paid", "is_test": False, "price": "1200", "qty": "2"},
{"order_id": "A002", "status": "cancelled","is_test": False, "price": "900", "qty": "1"},
{"order_id": "A003", "status": "paid", "is_test": True, "price": "300", "qty": "1"},
{"order_id": "A004", "status": "paid", "is_test": False, "price": None, "qty": "4"},
{"order_id": "A005", "status": "paid", "is_test": False, "price": "250", "qty": "3"},
]我們想做的目標是:
status == "paid" 且 is_test == False)price * qty)一樣把這幾個工具串在一起,就可以寫出一份優雅的資料分析:
3150
上述流程其實可以對應到 ETL 的思路:先過濾無效資料,再做欄位轉換,最後聚合產出指標。換句話說,filter + map + reduce 不是一場語法秀,而是一個可放進實際分析管線的骨架。
下列哪一個敘述最正確?
map 會回傳列表,因此可無限次重複迭代filter 與 map 在 Python 3 預設都回傳迭代器(iterator)reduce 是 Python 的 built-in,不需額外匯入lambda 可以寫多行 if/for 區塊答案:B
map 與 filter 在 Python 3 都回傳 iterator,通常會被消耗。reduce 位於 functools,需要 from functools import reduce。lambda 只能寫單一運算式。
給定 a = [1, 2, 3] 與 b = [10, 20],執行 list(map(lambda x, y: x + y, a, b)) 的結果為何?
[11, 22, 3]ValueError[11, 22][11, 22, None]答案:C
多個 iterable 傳給 map 時會平行取值,預設行為是以最短序列為準,因此只會產出兩筆。
下列哪個寫法最適合「空列表也要安全加總」的情境?
reduce(lambda acc, x: acc + x, nums, 0)reduce(lambda acc, x: acc + x, nums)sum = reduce(lambda acc, x: acc + x, nums)reduce(sum, nums)答案:A
提供 initial=0 可以避免空列表時拋出 TypeError,也是實務上最穩定的寫法。
請完成下列任務:
給定 orders,先保留 status == "paid" 且 is_test == False 的資料,再計算總營收 price * qty 加總。
請將以下 for 迴圈改寫為 map + filter(可搭配 lambda 或 def):
[1, 4, 9]
本篇從 lambda 出發,延伸到 map、filter 與 reduce,核心目標並不是追求短程式,而是希望建立一個可維護的資料處理思維。lambda 讓短邏輯可內嵌,map 負責一致轉換,filter 負責條件篩選,reduce 負責最終收斂。當這四者組合得當,就能把凌亂的資料處理程式改寫成語意清楚的管線!
lambda、map、filter、reduce 用途
| 工具 | 主要任務 | 輸入 | 輸出 | 常見用途 |
|---|---|---|---|---|
lambda |
定義短小匿名函式 | 參數 | 單一運算式結果 | 排序 key、一次性規則 |
map |
對每筆資料做同一轉換 | 函式 + iterable | iterator | 清理字串、型別轉換、特徵轉換 |
filter |
保留符合條件的資料 | 判斷函式 + iterable | iterator | 篩掉異常值、空值、測試資料 |
reduce |
將多筆資料收斂為單值 | 累積函式 + iterable (+ initial) | 單一值 | 加總、聚合、建立統計摘要 |
若能掌握先過濾、再轉換、最後收斂的概念,其實就已具備資料分析前處理最重要的基本功!