收銀機導出一串訂單,Maya 想按每杯單價排序。為一行比較專門寫個具名函式,好像有點小題大做。lambda 什麼時候才值得出場?
lambda 是一個小型匿名函式。map 和 filter 會把函式應用到序列上,但在 Python 3 中回傳的是惰性迭代器。想查看全部結果時,先轉成列表。操作很簡單時,推導式往往更清楚。 你還會看到 lambda 的延遲綁定、一次性迭代器,以及用 key 代替 map 後再排序。
lambda 是短函式
lambda 參數: 表達式 可以不用 def 程式碼區塊就建立函式。它適合作為一行規則傳給另一個函式。如果邏輯需要名字、多個步驟或文件字串,就改用 def。
double = lambda n: n * 2
print(double(4))
print((lambda a, b: a + b)(2, 3))小型匿名函式
map 轉換每個元素
map(function, iterable) 會對每個元素呼叫一次函式。Python 3 中結果是迭代器,所以要用 list() 包起來才能列印全部值。原序列不會被修改。
nums = [1, 2, 3]
print(list(map(lambda n: n + 1, nums)))
print(nums)惰性的 map
filter 保留比對項
filter(predicate, iterable) 只保留謂詞回傳 True 的元素。謂詞可以是 lambda,也可以是具名函式。列印或索引前,先把結果轉成列表。
nums = [1, 2, 3, 4]
even = filter(lambda n: n % 2 == 0, nums)
print(list(even))保留符合條件的值
lambda 在呼叫時才查變數
lambda 在被呼叫時才尋找自由變數,而不是建立時。迴圈裡建立的多個 lambda 都引用同一個 n,就都會看到最後的值。需要各自儲存目前值時,用預設參數捕獲。
funcs = [lambda: n for n in range(3)]
print([fn() for fn in funcs])
fixed = [lambda n=n: n for n in range(3)]
print([fn() for fn in fixed])用預設參數綁定目前迴圈值
map 和 filter 只能消費一次
map() 和 filter() 回傳迭代器,不是列表。消費完就耗盡。需要查看或重複使用時轉成列表;不需要時,直接交給 sum() 或 any(),它們也只消費一次。
numbers = map(lambda n: n * 2, [1, 2, 3])
print(list(numbers))
print(list(numbers))
filtered = filter(lambda n: n > 1, [1, 2, 3])
print(any(filtered))
print(any(filtered))耗盡的迭代器不會重新開始
用 key,不要先 map 再排序
只需要極值或順序時,把 key 函式直接交給 min、max 或 sorted。單獨構造一個映射列表會多一步,還可能丟掉原記錄。lambda 只寫一個短表達式;規則需要名字時改用 def。
orders = [("latte", 4.5), ("tea", 3.0), ("mocha", 5.0)]
cheapest = min(orders, key=lambda item: item[1])
names = sorted(orders, key=lambda item: item[1], reverse=True)
print(cheapest)
print([name for name, price in names])保留記錄,傳入規則
本課要點
- lambda 用來建立小型匿名函式。
- map 對每個元素應用函式,並回傳迭代器。
- filter 保留謂詞為真的元素。
- 列表推導式通常比 map 或 filter 更容易讀。
- lambda 延遲綁定;迭代器只消費一次;key 函式能保留原記錄。
常見錯誤
- 直接列印 map 或 filter 物件,卻期待看到列表。
- 邏輯很複雜時仍然硬寫 lambda,而不是用具名函式。
- 忘記 map 和 filter 在被消費前是惰性的。
- 對映射後的價格列表排序,結果丟掉了飲料名。
用 map 和 filter 把 [1, 2, 3, 4, 5] 中的偶數平方。
動手試試
改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。
練習本課
12 道題,作答後立即看到解釋。