lambda 表达式
lambda 关键字用于创建匿名函数——没有名称、只能包含单个表达式的小型函数。它在语法上是 def 语句的受限版本,但在语义上等价于一个嵌套函数定义。lambda 的设计初衷是提供一种简洁的、内联的函数书写方式,用于那些"只使用一次"的简单场景。
lambda 的基本语法
lambda 表达式的语法为 lambda 参数: 表达式。参数列表与普通函数相同(支持默认参数、*args、**kwargs),但冒号后只能有一个表达式,不能包含语句:
# 普通函数
def add(x, y):
return x + y
# 等价的 lambda
add_lambda = lambda x, y: x + y
print(add(2, 3)) # 5
print(add_lambda(2, 3)) # 5
lambda 表达式本身求值为一个函数对象,因此可以立即调用:
print((lambda x: x * x)(5)) # 25
lambda 的限制
lambda 比普通函数严格得多,只能包含一个表达式,不能包含以下任何内容:
- 多条语句
- 赋值语句(
=) return、yield、pass、assertraise(虽然某些情况下可以,但不推荐)- 类型注解
- 文档字符串
# 非法:lambda 不能包含赋值
# lambda x: y = x + 1; return y
# 非法:lambda 不能包含多条语句
# lambda x: print(x); x + 1
# 合法:表达式可以调用函数
lambda x: print(x) or x + 1 # 利用 or 的短路特性,但可读性差
如果需要复杂逻辑,应该使用 def 定义命名函数。lambda 的价值在于简洁,一旦变得晦涩,就失去了意义。
lambda 与 def 的本质区别
从语义上看,lambda 和 def 创建的函数对象几乎相同,但有几点差异:
- 名称:
lambda函数对象的__name__属性固定为<lambda>,不利于调试:
f = lambda x: x + 1
print(f.__name__) # '<lambda>'
def g(x):
return x + 1
print(g.__name__) # 'g'
作用域:两者都遵循相同的 LEGB 规则,都可以引用外层变量形成闭包。
语法位置:
lambda是表达式,可以出现在任何需要值的地方;def是语句,只能出现在语句位置。
排序中的使用
lambda 最常见的用途之一是为 sorted() 和 list.sort() 提供 key 函数:
students = [
("Alice", 85),
("Bob", 92),
("Charlie", 78),
]
# 按分数(第二个元素)排序
by_score = sorted(students, key=lambda s: s[1])
print(by_score)
# [('Charlie', 78), ('Alice', 85), ('Bob', 92)]
# 按分数降序
by_score_desc = sorted(students, key=lambda s: s[1], reverse=True)
print(by_score_desc)
# [('Bob', 92), ('Alice', 85), ('Charlie', 78)]
对于更复杂的排序规则,lambda 可以返回元组实现多级排序:
# 先按分数降序,分数相同按名字升序
students = [("Alice", 85), ("Bob", 92), ("Charlie", 85)]
result = sorted(students, key=lambda s: (-s[1], s[0]))
print(result)
# [('Bob', 92), ('Alice', 85), ('Charlie', 85)]
映射与过滤
lambda 也常与 map()、filter() 配合使用:
nums = [1, 2, 3, 4, 5]
# 映射:每个元素平方
squares = list(map(lambda x: x ** 2, nums))
print(squares) # [1, 4, 9, 16, 25]
# 过滤:保留偶数
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens) # [2, 4]
不过,现代 Python 更推荐使用列表推导式,通常比 map/filter 加 lambda 更易读:
# 列表推导式等价写法
squares = [x ** 2 for x in nums]
evens = [x for x in nums if x % 2 == 0]
闭包中的 lambda
lambda 可以引用包含作用域中的变量,形成闭包:
def make_multiplier(n):
return lambda x: x * n
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
但这里有一个经典陷阱:在循环中创建 lambda 时,所有 lambda 共享同一个变量引用:
# 错误示范
funcs = []
for i in range(3):
funcs.append(lambda: i)
print(funcs[0]()) # 2,不是 0!
print(funcs[1]()) # 2,不是 1!
print(funcs[2]()) # 2
原因是 lambda 捕获的是变量 i 的引用,而不是当时的值。循环结束时 i 为 2,所以所有 lambda 都返回 2。修复方法是利用默认参数在定义时求值的特性:
# 正确做法
funcs = []
for i in range(3):
funcs.append(lambda x=i: x) # 默认参数在定义时绑定当前值
print(funcs[0]()) # 0
print(funcs[1]()) # 1
print(funcs[2]()) # 2
常见错误
试图在 lambda 中写多条语句:
# lambda x: x += 1; return x # SyntaxError
过度使用 lambda 导致可读性下降:
# 不推荐:复杂的 lambda 难以阅读
process = lambda data: [x.strip().lower() for x in data if x and len(x) > 3]
# 推荐:用 def 命名,语义清晰
def process(data):
return [x.strip().lower() for x in data if x and len(x) > 3]
小结
lambda 是创建小型匿名函数的语法糖,只能包含单个表达式,没有名称和文档字符串。它适合排序 key、简单映射等一次性场景。一旦逻辑复杂,应改用 def。理解 lambda 与 def 的等价性,以及闭包中的变量捕获陷阱,才能正确使用这个工具。