匿名函数
约 1107 字大约 4 分钟
2025-04-04
阅读导览
匿名函数
lambda 是 Python 里的“单行函数”:只能写一个表达式、自动返回它。它适合做 sorted/filter/map 的 key 函数和回调,不适合承载真正的逻辑。这一节既讲怎么写,也讲什么时候别写。
- lambda 的参数与表达式
- 适用场景:sorted/filter/map 的 key/predicate
- 最常见陷阱:循环中绑定变量(晚绑定)
- 用默认参数 lambda x=i: x 固定当前值
- PEP 8 不推荐 f = lambda ...:要起名就用 def
实践清单
学习练习清单
- 1写 sorted(records, key=lambda r: r['age']) 这种 key 函数 —— 这是 lambda 最正当的用途。
- 2故意跑 [lambda: i for i in range(3)] 然后调用,看到全是 2,再用 lambda i=i: i 修复,体会“晚绑定陷阱”。
- 3把 f = lambda x, y: x + y 改成 def add(x, y): return x + y —— 用 def 取名几乎总是更好。
- 4记住:lambda 体内不能写 if/return/assert/yield 等语句,超出表达式范围就要用 def。
匿名函数的关键字是 lambda。匿名函数在高阶函数中的应用非常广泛,它能够极大地简化代码结构。
例如,以下是一个经典的函数定义与调用的代码示例:
python playground
def func(a, b):
c = a + b
return c
print(func(1, 2))如果使用匿名函数,只需要两行代码即可完成同样的功能:
python playground
f = lambda a, b: a + b
print(f(1, 2))甚至可以进一步压缩为一行代码:
python playground
print((lambda a, b: a + b)(1, 2))在上述代码中:
lambda的作用类似于def,用于声明一个函数。a, b和普通函数中的(a, b)含义相同,用于定义形参。: a + b的作用相当于普通函数中的return a + b,用于指定返回值。
匿名函数支持多种形式的参数传递,包括位置参数、动态位置参数、默认参数以及动态关键字参数,也可以不包含任何参数。
需要注意的是,匿名函数的冒号 : 后面紧跟的是函数的返回值部分,且必须返回一个单一的数据值。
当匿名函数与 for 循环结合使用时,会遇到一个常见的陷阱:如果先进行循环,再调用匿名函数,那么循环变量会被绑定到最后一次迭代的值,而不是每次迭代时的值。例如:
python playground
lst = [] # [func, func, func]
for i in range(3):
lst.append(lambda: i)
for j in lst:
print(j()) # 三次循环打印的都是 2当第一个循环结束时,列表中存储的是三个匿名函数的引用地址。每个匿名函数内部实际上只有一个内容:返回变量 i。然而,在调用这些函数之前,代码并不会执行,而此时 i 的值已经变成了 2。虽然前两个函数被加入列表时,i 的值分别是 0 和 1,但它们并未记录当时的值。
为了更直观地理解这个问题,我们可以将上述代码改写为普通函数的形式:
python playground
lst = []
for i in range(3):
def func():
return i
lst.append(func)
for j in lst:
print(j())要解决这个问题,可以通过为匿名函数添加默认参数的方式,将当前循环变量的值“固定”下来:
python playground
lst = [] # [func, func, func]
for i in range(3):
lst.append(lambda x = i: x)
for j in lst:
print(j())如果将匿名函数与列表推导式结合使用,问题可能会显得更加隐蔽:
python playground
g = [lambda: i + 1 for i in range(3)]
print([em() for em in g]) # [3, 3, 3]而对于生成器表达式,情况则有所不同——因为生成器的惰性求值特性,只有在函数被取出时,循环才会继续向下执行:
python playground
g = (lambda: i + 1 for i in range(3))
print([em() for em in g]) # [1, 2, 3]重点总结
要点回收
- lambda 只能写一个表达式 —— 想写多行/分支/yield,直接换成 def。
- 正经用途是 sorted/filter/map 的 key/predicate,以及短小的回调。
- 循环里写 lambda 注意晚绑定:lambda x=i: x 用默认参数把当前值“锁”进去。
- 需要起名字的函数永远用 def —— 起名才能在 traceback、help、调试器里看到含义。
版权所有
版权归属:Shuo Liu
