Python闭包中修改外层变量需用nonlocal声明,否则赋值会触发UnboundLocalError;因赋值使变量默认为局部变量,而读取时按LEGB规则查找,nonlocal显式声明可变闭包以保障代码明确性。

因为 Python 默认把函数内赋值的变量当作局部变量,即使同名变量在外部作用域已存在,不加 nonlocal 就无法在闭包中修改外层函数的变量。
作用域规则决定了赋值行为
Python 遵循 LEGB 规则(Local → Enclosing → Global → Built-in),但只读访问和写入访问的处理方式不同:
- 读取变量时,会按 LEGB 顺序向上查找
- 一旦在函数体内对某个变量执行了赋值(如
x = x + 1),Python 就默认这个变量是当前函数的局部变量 - 此时若该变量在外部作用域存在,但未声明为
nonlocal或global,就会报UnboundLocalError
闭包中的典型错误示例
比如想实现一个计数器:
def make_counter():
count = 0
def counter():
count += 1 # ❌ 报错:UnboundLocalError
return count
return counter
c = make_counter()
c() # UnboundLocalError: local variable 'count' referenced before assignment
这里 count += 1 等价于 count = count + 1,触发了“赋值即局部变量”的规则。虽然读取 count 时能找到外层的 count,但一赋值就要求它必须是局部变量——而它又没被初始化,所以出错。
立即学习“Python免费学习笔记(深入)”;
nonlocal 的作用就是显式声明“我要修改的是外层非全局变量”
修正方式很简单:
def make_counter():
count = 0
def counter():
nonlocal count # ✅ 告诉 Python:这个 count 是外层函数的变量
count += 1
return count
return counter-
nonlocal只能用于嵌套函数中,且目标变量必须存在于外层(enclosing)作用域,不能是全局变量 - 如果是想改模块级变量,要用
global,而不是nonlocal -
nonlocal声明后,对该变量的读、写、del都操作的是外层变量,真正构成可变闭包
为什么设计成这样?
这是 Python 明确性原则的体现:
- 避免无意中修改外层状态,提升代码可预测性
- 强制开发者意识到“我在跨作用域修改”,而不是隐式地影响上层逻辑
- 区分只读闭包(无需声明)和可变闭包(需显式声明),语义更清晰










