一等函数(First-class Functions)
在 Python 里,函数就是对象,和整数、字符串一样能被:
• 赋值给变量
• 作为参数传入
• 作为返回值返回
• 放进列表/字典等数据结构
bash
def apply_twice(f, x):
return f(f(x)) # 函数当参数
inc = lambda v: v + 1 # 赋值给变量
print(apply_twice(inc, 3)) # 5
有了"一等函数",才有高阶函数、装饰器等强大抽象。
闭包(Closure)
定义:内层函数记住并携带了其外层作用域的变量(自由变量),即使外层函数已经返回,仍可使用这些变量。
满足三件事就是闭包:
- 有内层函数;2) 内层函数用到了外层的局部变量;3) 外层函数已经返回。
bash
def make_adder(n):
def add(x):
return x + n # 用到外层变量 n
return add # 返回内层函数(形成闭包)
add5 = make_adder(5)
print(add5(10)) # 15
print(add5.__closure__[0].cell_contents) # 5(看看捕获的值)
修改外层变量:nonlocal
闭包里如需重新绑定外层局部变量,用 nonlocal(全局用 global)。
bash
def counter():
c = 0
def inc():
nonlocal c
c += 1
return c
return inc
inc = counter()
print(inc(), inc(), inc()) # 1 2 3
经典坑:循环里的"晚绑定"(late binding)
闭包捕获的是变量引用,不是当时的值:
bash
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2] 全都变成最后的 i
修法(任选其一):
bash
# 1) 默认参数"定格"当前值
funcs = [lambda i=i: i for i in range(3)]
# 2) 再包一层工厂函数
funcs = []
for i in range(3):
def make(j):
return lambda: j
funcs.append(make(i))
# 3) functools.partial
from functools import partial
def identity(x): return x
funcs = [partial(identity, i) for i in range(3)]
装饰器其实就是闭包
bash
import time
def timing(fn): # 外层工厂,捕获 fn
def wrapper(*args, **kwargs):
t0 = time.perf_counter()
try:
return fn(*args, **kwargs)
finally:
dt = time.perf_counter() - t0
print(f"{fn.__name__} took {dt:.3f}s")
return wrapper
@timing
def work(n):
sum(range(n))
work(1000000) # 自动打印耗时
小结
• 一等函数:函数是"值",能传来传去。
• 闭包:函数能"带着环境里的变量一起走"。
配合 nonlocal 可更新状态;注意循环晚绑定,用"默认参数/工厂函数/partial"规避。
这些机制是 Python 中高阶抽象、装饰器、回调、工厂函数的基石。