在 Python 中,闭包指内层函数在外层函数返回之后,仍能访问并使用外层作用域中的变量。计数器、带参数的工厂函数以及装饰器中的 wrapper,均依赖这一机制。若内层函数需要修改被捕获的外层变量,须使用 nonlocal 声明。本节说明闭包的构成条件、典型用法及其与装饰器的关系。
携带状态的闭包
def make_counter():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
c = make_counter()
print(c())
print(c())
print(c())make_counter 返回后,局部变量 count 因仍被 inner 引用而保留。每次调用 c(),在同一 count 上累加。
构成条件
- 存在嵌套函数。
- 内层函数引用外层函数的变量。
- 外层函数将内层函数作为返回值传出(或保存到别处)。
make_counter() 调用结束后,count 仍被 inner 引用而保留;每次 c() 在同一 count 上递增。
def outer(x):
def inner(y):
return x + y
return inner
add5 = outer(5)
add10 = outer(10)
print(add5(3))
print(add10(3))add5 与 add10 为两个独立闭包,各自记住不同的 x。
与模块级全局变量对比
将配置封入闭包,可避免在模块顶层维护可变全局状态,且可同时存在多个独立配置:
def make_mul(factor):
def mul(x):
return x * factor
return mul
double = make_mul(2)
triple = make_mul(3)
print(double(5))
print(triple(5))工厂函数:延迟绑定参数
先传入问候语得到专用问候函数,调用时再传入人名:
def make_greeter(greeting):
def greet(name):
return f"{greeting}, {name}!"
return greet
say_hi = make_greeter("你好")
say_morning = make_greeter("早上好")
print(say_hi("小明"))
print(say_morning("小红"))与装饰器的关系
装饰器返回的 wrapper 捕获原函数 func,即闭包。装饰器一节中的日志、计时包装均可从此理解:
def log(func):
def wrapper(*args, **kwargs):
print("调用前")
result = func(*args, **kwargs)
return result
return wrapper
@log
def hello():
print("hi")
hello()常见错误
- 内层须修改外层变量却未声明
nonlocal时,赋值仅创建内层局部名,外层变量不变。 - 在循环中批量创建闭包且直接引用循环变量时,捕获的是变量本身;循环结束后调用可能均得到最终值。须冻结当时值时,可用默认参数,例如
lambda n=i: n。
本节说明了 Python 中闭包的含义、构成及与装饰器的关系。下一章见 Python 标准库参考。