在 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 = 0 外层变量(调用完不消失) inner() 内层函数 count += 1 return 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 标准库参考。