跳到主要内容

什么是 Python 的闭包?

定义​

闭包(Closure)是指一个函数记住了其定义时所在作用域的变量,即使该作用域已经执行完毕、不再存在,这些变量依然可以被内层函数访问和使用。

构成闭包的三个条件:

  1. 存在嵌套函数(函数内定义函数)
  2. 内层函数引用了外层函数的变量(自由变量)
  3. 外层函数将内层函数作为返回值返回

基础示例​

def outer(x):
def inner(y):
return x + y # inner 引用了外层变量 x
return inner # 返回内层函数

add5 = outer(5) # outer 执行完毕,但 x=5 被"记住"
print(add5(3)) # 8
print(add5(10)) # 15

add5 就是一个闭包,它携带着 x=5 这个自由变量。

查看闭包捕获的变量​

print(add5.__closure__) # (<cell at 0x...>,)
print(add5.__closure__[0].cell_contents) # 5

常见陷阱:循环中的闭包​

# ❌ 错误:所有函数共享同一个 i
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2]

# ✅ 正确:用默认参数将 i 的值固定在每次迭代
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]

原因:闭包捕获的是变量本身(引用),而非变量的值。循环结束后 i=2,所有 lambda 都读到同一个 i。

nonlocal 关键字​

在闭包内部修改外层变量时,需要使用 nonlocal 声明:

def counter():
count = 0
def increment():
nonlocal count # 声明修改外层变量
count += 1
return count
return increment

c = counter()
print(c()) # 1
print(c()) # 2
print(c()) # 3

不加 nonlocal 直接赋值会报 UnboundLocalError,因为 Python 会将其视为局部变量。

典型应用场景​

1. 函数工厂​

def make_multiplier(n):
def multiplier(x):
return x * n
return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15

2. 装饰器(本质就是闭包)​

def logger(func):
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper

@logger
def greet(name):
print(f"Hello, {name}!")

greet("Alice")
# 调用 greet
# Hello, Alice!

3. 延迟计算 / 缓存状态​

def make_adder(base):
history = []
def add(x):
result = base + x
history.append(result)
return result, history
return add

adder = make_adder(10)
print(adder(1)) # (11, [11])
print(adder(2)) # (12, [11, 12])

闭包 vs 类​

闭包类
状态存储自由变量实例属性
代码量少,轻量多,结构清晰
适用场景简单状态、单一行为复杂状态、多个方法

总结​

  • 闭包 = 内层函数 + 捕获的外层自由变量
  • 核心价值:让函数携带状态,是装饰器、函数工厂等模式的基础
  • 注意循环中的变量捕获陷阱,修改外层变量需用 nonlocal