跳到主要内容

Lambda 匿名函数

lambda 是 Python 中创建匿名函数(anonymous function)的关键字。它适合定义简单、一次性使用的函数,常用于 sortedmapfilter 等需要函数作为参数的场景。理解 lambda 的语法和适用范围能帮助你写出更简洁的代码。

lambda 语法

lambda 表达式的语法非常简洁:

lambda 参数列表: 表达式

注意:lambda 主体是单个表达式,不能包含语句(如 if/while/for/def/return)。

# 等价于 def add(a, b): return a + b
add = lambda a, b: a + b
print(add(3, 5)) # 输出:8

# 单参数
square = lambda x: x ** 2
print(square(5)) # 输出:25

# 无参数
greet = lambda: "Hello!"
print(greet()) # 输出:Hello!

# 多参数
multiply = lambda x, y, z: x * y * z
print(multiply(2, 3, 4)) # 输出:24

带默认值的 lambda:

power = lambda x, n=2: x ** n
print(power(3)) # 输出:9(默认 n=2)
print(power(3, 4)) # 输出:81

带类型注解的 lambda(Python 3.12+ 中类型系统支持 lambda 注解,但实际很少用):

# 一般 lambda 不加类型注解,依靠上下文判断
concat = lambda a, b: f"{a}-{b}"
print(concat("x", "y")) # 输出:x-y

与 def 的区别

特性def 定义lambda 表达式
语句 vs 表达式是语句是表达式
函数体多条语句,可有逻辑分支单个表达式
函数名必须有名字匿名(可赋给变量)
文档字符串支持 __doc__不支持
装饰器支持 @decorator不支持
可读性适合复杂逻辑适合简短逻辑
# def 版本
def get_full_name(first, last):
return f"{first} {last}"

# lambda 版本
get_full_name_lambda = lambda first, last: f"{first} {last}"

print(get_full_name("Alice", "Smith")) # 输出:Alice Smith
print(get_full_name_lambda("Alice", "Smith")) # 输出:Alice Smith

# 文档字符串差异
print(get_full_name.__doc__) # 输出:None(未定义时)
print(get_full_name_lambda.__doc__) # 输出:None(lambda 不支持 docstring)

:::warning 不要滥用 lambda 将 lambda 赋值给变量后当作普通函数使用,是一种反模式(PEP 8 不推荐):

# 不推荐
f = lambda x: x * 2

# 推荐
def f(x):
return x * 2

原因:def 版本支持文档字符串、更好的堆栈跟踪、IDE 提示更友好。lambda 真正适合的场景是就地使用,例如作为参数传给 sorted/map/filter。 :::

作为参数传递

lambda 最大的用武之地是作为高阶函数的参数。

sorted 的 key

students = [
{"name": "Alice", "score": 88},
{"name": "Bob", "score": 95},
{"name": "Charlie", "score": 72},
]

# 按 score 升序
sorted_by_score = sorted(students, key=lambda s: s["score"])
print(sorted_by_score)
# 输出:[{'name': 'Charlie', 'score': 72}, {'name': 'Alice', 'score': 88}, {'name': 'Bob', 'score': 95}]

# 按 name 长度降序
sorted_by_name_len = sorted(students, key=lambda s: len(s["name"]), reverse=True)
print([s["name"] for s in sorted_by_name_len])
# 输出:['Charlie', 'Alice', 'Bob']

map 转换序列

numbers = [1, 2, 3, 4, 5]

squares = list(map(lambda x: x ** 2, numbers))
print(squares) # 输出:[1, 4, 9, 16, 25]

# 同时对多个序列操作
a = [1, 2, 3]
b = [10, 20, 30]
sums = list(map(lambda x, y: x + y, a, b))
print(sums) # 输出:[11, 22, 33]

filter 过滤序列

numbers = list(range(1, 11))

# 过滤偶数
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # 输出:[2, 4, 6, 8, 10]

# 过滤大于 5 的数
big = list(filter(lambda x: x > 5, numbers))
print(big) # 输出:[6, 7, 8, 9, 10]

:::info map/filter vs 列表推导式 虽然 mapfilter 配合 lambda 很常见,但 Python 社区更推荐使用列表推导式,因为可读性更好:

# map/filter 写法
squares = list(map(lambda x: x ** 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))

# 列表推导式写法(更 Pythonic)
squares = [x ** 2 for x in numbers]
evens = [x for x in numbers if x % 2 == 0]

:::

常用场景

1. 排序 key

words = ["banana", "apple", "cherry", "date"]

# 按字母顺序
words_sorted = sorted(words, key=lambda w: w.lower())
print(words_sorted) # 输出:['apple', 'banana', 'cherry', 'date']

# 按长度,长度相同按字母
words_sorted2 = sorted(words, key=lambda w: (len(w), w))
print(words_sorted2) # 输出:['date', 'apple', 'banana', 'cherry']

2. 字典默认值

from collections import defaultdict

# 默认值为空列表
counts = defaultdict(lambda: 0)
for word in ["a", "b", "a", "c", "b", "a"]:
counts[word] += 1
print(dict(counts)) # 输出:{'a': 3, 'b': 2, 'c': 1}

# 默认值为自定义对象
matrix = defaultdict(lambda: defaultdict(int))
matrix[0][0] = 1
matrix[1][2] = 5
print(dict(matrix[0])) # 输出:{0: 1}
print(dict(matrix[1])) # 输出:{2: 5}

3. GUI/事件回调

# 模拟 GUI 按钮回调(简化示例)
def create_button(label, on_click):
print(f"按钮 [{label}] 创建,回调已注册")
return on_click

button_callback = create_button("提交", lambda: print("按钮被点击了"))
# 后续触发
button_callback() # 输出:按钮被点击了

4. 元素取值

data = [("Alice", 30), ("Bob", 25), ("Charlie", 35)]

# 按第二个元素排序
data_sorted = sorted(data, key=lambda item: item[1])
print(data_sorted)
# 输出:[('Bob', 25), ('Alice', 30), ('Charlie', 35)]

局限性

lambda 也有不少限制:

1. 不能包含语句

# 错误:lambda 不能包含赋值语句
# f = lambda x: (y = x + 1; y * 2) # SyntaxError

# 替代:用海象运算符(Python 3.8+)
f = lambda x: (y := x + 1) * 2 # 海象运算符可以在表达式内赋值
print(f(5)) # 输出:12

:::tip 海象运算符 := := 是 Python 3.8 引入的赋值表达式,可以在表达式内部赋值。配合 lambda 能突破部分限制:

# 计算平方并返回原数和平方
transform = lambda x: (x, (sq := x ** 2))
print(transform(5)) # 输出:(5, 25)

:::

2. 不能有分支逻辑(除非用三元表达式)

# 用三元表达式模拟 if-else
classify = lambda x: "正" if x > 0 else ("零" if x == 0 else "负")
print(classify(5)) # 输出:正
print(classify(0)) # 输出:零
print(classify(-3)) # 输出:负

# 太复杂时应改用 def

3. 没有文档字符串

lambda 不支持 __doc__,调用 help() 时显示的只是 <lambda>

f = lambda x: x * 2
help(f)
# 输出:<lambda> function

4. 不能被装饰

# 不能直接对 lambda 应用 @decorator 语法
# 必须用 def 才能用 @ 语法

与列表推导式对比

很多场景下,列表推导式比 map/filter + lambda 更直观:

numbers = list(range(1, 6))

# map + lambda
squares_1 = list(map(lambda x: x ** 2, numbers))

# 列表推导式
squares_2 = [x ** 2 for x in numbers]

# 两者结果相同
print(squares_1 == squares_2) # 输出:True

性能对比:

import timeit

setup = "numbers = list(range(1000))"

# map + lambda
t1 = timeit.timeit("list(map(lambda x: x ** 2, numbers))", setup=setup, number=10000)

# 列表推导式
t2 = timeit.timeit("[x ** 2 for x in numbers]", setup=setup, number=10000)

print(f"map + lambda: {t1:.3f}s")
print(f"列表推导式: {t2:.3f}s")

通常列表推导式更快、更易读,但 map 在配合内置函数(如 strint)时仍然简洁:

# 这种情况下 map 更简洁
strings = ["1", "2", "3"]
nums = list(map(int, strings)) # 直接传 int,无需 lambda
print(nums) # 输出:[1, 2, 3]

# 等价列表推导式
nums = [int(s) for s in strings]

:::info 选择建议

  • 简单转换:列表推导式更 Pythonic
  • 配合已有函数(无 lambda):map/filter 仍然简洁
  • 复杂逻辑:使用 def 定义具名函数 :::

operator 模块

operator 模块提供了对应内置运算符的函数,能替代部分 lambda,让代码更清晰:

import operator

# 算术运算
print(operator.add(3, 5)) # 输出:8 等价 lambda a, b: a + b
print(operator.sub(10, 4)) # 输出:6
print(operator.mul(3, 4)) # 输出:12
print(operator.truediv(10, 4)) # 输出:2.5
print(operator.mod(10, 3)) # 输出:1
print(operator.pow(2, 10)) # 输出:1024

# 比较
print(operator.eq(3, 3)) # 输出:True
print(operator.lt(3, 5)) # 输出:True
print(operator.gt(3, 5)) # 输出:False

itemgetter 与 attrgetter

itemgetter 替代 lambda x: x[key]attrgetter 替代 lambda x: x.attr

from operator import itemgetter, attrgetter

students = [
{"name": "Alice", "score": 88},
{"name": "Bob", "score": 95},
{"name": "Charlie", "score": 72},
]

# 用 lambda
sorted1 = sorted(students, key=lambda s: s["score"])

# 用 itemgetter(更高效,因为内部用 C 实现)
sorted2 = sorted(students, key=itemgetter("score"))

print([s["name"] for s in sorted1]) # 输出:['Charlie', 'Alice', 'Bob']
print([s["name"] for s in sorted2]) # 输出:['Charlie', 'Alice', 'Bob']

# 多字段排序
multi = sorted(students, key=itemgetter("score", "name"))

对对象属性排序:

class Student:
def __init__(self, name, score):
self.name = name
self.score = score

def __repr__(self):
return f"Student({self.name!r}, {self.score})"

students = [
Student("Alice", 88),
Student("Bob", 95),
Student("Charlie", 72),
]

# 用 attrgetter 替代 lambda s: s.score
sorted_students = sorted(students, key=attrgetter("score"))
print(sorted_students)
# 输出:[Student('Charlie', 72), Student('Alice', 88), Student('Bob', 95)]

:::tip itemgetter 性能更好 itemgetterattrgettermethodcaller 由 C 实现,比 lambda 略快。在性能敏感场景下推荐使用。 :::

methodcaller

from operator import methodcaller

words = ["Hello", "WORLD", "Python"]
uppercased = list(map(methodcaller("upper"), words))
print(uppercased) # 输出:['HELLO', 'WORLD', 'PYTHON']

# 等价于
uppercased = list(map(lambda s: s.upper(), words))

实战:按多字段排序

下面实现一个综合示例,按多个字段对员工数据进行排序,并对比 lambda 和 operator 的写法:

from operator import itemgetter

employees = [
{"name": "Alice", "department": "工程", "salary": 25000, "level": 3},
{"name": "Bob", "department": "工程", "salary": 28000, "level": 4},
{"name": "Charlie", "department": "销售", "salary": 22000, "level": 3},
{"name": "Diana", "department": "销售", "salary": 28000, "level": 4},
{"name": "Eve", "department": "工程", "salary": 28000, "level": 3},
]


def sort_employees(emps: list[dict], *fields: str, reverse: bool = False) -> list[dict]:
"""按多个字段排序员工列表。

Args:
emps: 员工列表。
*fields: 排序字段,按优先级排列。
reverse: 是否降序。

Returns:
排序后的新列表。
"""
if not fields:
return list(emps)
# 使用 itemgetter 一次取出多个字段
key = itemgetter(*fields) if len(fields) > 1 else itemgetter(fields[0])
return sorted(emps, key=key, reverse=reverse)


# 1. 按部门升序
by_dept = sort_employees(employees, "department")
print("按部门:", [e["name"] for e in by_dept])
# 输出:['Alice', 'Bob', 'Eve', 'Charlie', 'Diana']

# 2. 按部门、再按薪资降序(同部门内薪资高在前)
by_dept_salary = sorted(
employees,
key=lambda e: (e["department"], -e["salary"]),
)
print("按部门+薪资降序:")
for e in by_dept_salary:
print(f" {e['department']:4s} {e['name']:8s} ¥{e['salary']}")
# 输出:
# 工程 Bob ¥28000
# 工程 Eve ¥28000
# 工程 Alice ¥25000
# 销售 Diana ¥28000
# 销售 Charlie ¥22000

# 3. 按级别、再按薪资(都升序),用 itemgetter
by_level_salary = sorted(employees, key=itemgetter("level", "salary"))
print("按级别+薪资:")
for e in by_level_salary:
print(f" L{e['level']} {e['name']:8s} ¥{e['salary']}")
# 输出:
# L3 Charlie ¥22000
# L3 Alice ¥25000
# L3 Eve ¥28000
# L4 Bob ¥28000
# L4 Diana ¥28000

:::tip 多字段排序技巧

  • 同方向(都升序/都降序):用 itemgetter("a", "b") 一次取出元组
  • 混合方向:数值字段可用 -value 反转,字符串字段较复杂,需多次 sorted(Python 的 sorted 是稳定排序)
# 多次稳定排序:先按次要字段,再按主要字段
by_secondary = sorted(employees, key=lambda e: e["salary"]) # 先按薪资
by_primary = sorted(by_secondary, key=lambda e: e["department"]) # 再按部门

:::

小结

  • lambda 参数: 表达式 创建匿名函数,主体只能是单个表达式
  • def 相比,lambda 简洁但缺少文档字符串、不支持装饰器、不适合复杂逻辑
  • lambda 最适合用作高阶函数(sorted/map/filter/defaultdict)的参数
  • 简单转换场景下,列表推导式通常比 map + lambda 更 Pythonic
  • operator 模块(itemgetter/attrgetter/methodcaller)能替代常用 lambda,更高效更清晰
  • 不要把 lambda 赋值给变量当作普通函数使用,那样做不如 def

下一节将深入 Python 的作用域规则 LEGB,理解变量的查找顺序。