
Python闭包允许内层函数“记住”外层函数的变量,但仅支持只读访问;若需修改,必须显式声明nonlocal,否则会触发UnboundLocalError——这是由Python的变量作用域规则和闭包实现机制共同决定的。
python闭包允许内层函数“记住”外层函数的变量,但仅支持**只读访问**;若需修改,必须显式声明`nonlocal`,否则会触发`unboundlocalerror`——这是由python的变量作用域规则和闭包实现机制共同决定的。
在Python中,闭包(Closure)的本质是函数对象与其定义时捕获的自由变量(free variables)环境的组合体。当一个嵌套函数引用了外层函数的局部变量,并且外层函数返回该嵌套函数时,Python会自动将这些被引用的变量封装进__closure__属性所指向的cell对象中,从而延长其生命周期——即使外层函数已执行完毕、其栈帧销毁,这些变量仍可通过闭包持续访问。
然而,可访问 ≠ 可赋值。关键在于Python的变量绑定规则:
- 若内层函数仅读取外层变量(如
return b + 1),Python沿LEGB规则向上查找,成功定位到闭包中的b,访问cell_contents即可; - 但若尝试写入(如
b += 1),Python在编译阶段即判定:该变量在当前作用域(B函数体内)有赋值操作 → 自动将其视为局部变量; - 此时
b += 1等价于b = b + 1,前半部分b试图读取尚未初始化的局部变量,而并非去闭包中查找——于是抛出UnboundLocalError: local variable 'b' referenced before assignment。
✅ 正确修改闭包变量的方式是使用 nonlocal 声明:
def A():
b = 1
def B():
nonlocal b # 显式声明:b 来自外层非全局作用域
b += 1
return b
return B() # 注意:此处调用 B(),返回的是数值
print(A()) # 输出:2⚠️ 注意事项:
立即学习“Python免费学习笔记(深入)”;
-
global适用于修改模块级变量,不适用于闭包场景; -
nonlocal只能向上一层或指定层作用域查找,不能跳过中间层; - 若未加
nonlocal却尝试赋值,Python不会“降级”去闭包中找,而是严格按局部→闭包→全局→内置的顺序在运行时查找——但赋值行为本身已在编译期锁定作用域。
? 验证闭包结构(加深理解):
def A():
b = 1
def B():
nonlocal b
b += 1
return b
return B
func = A()
print(func.__closure__) # (<cell at 0x...: int object at 0x...>,)
print(func.__closure__[0].cell_contents) # 1(初始值)
func() # 修改后 b 变为 2
print(func.__closure__[0].cell_contents) # 2? 为什么设计成“只读默认”?
这保障了闭包的确定性与可预测性。例如工厂函数生成多个独立闭包时:
def make_adder(x):
return lambda y: x + y
add5 = make_adder(5) # 闭包中 x=5
add10 = make_adder(10) # 闭包中 x=10
print(add5(3)) # 8
print(add10(3)) # 13每个闭包都固化了自己的x值,互不干扰。若允许隐式修改,将破坏这种隔离性,引发难以追踪的状态污染。
总结:闭包不是“变量引用”,而是对自由变量的只读快照 + 显式可变通道(via nonlocal)。理解这一机制,是掌握装饰器、状态封装、回调配置等高级模式的基石。


















