nonlocal作用域边界是直接外层函数(enclosing scope),仅限一层,不能跨层、不能用于全局或类方法;需外层已定义同名变量,且首次读取前必须已初始化。

nonlocal 作用域边界在哪?nonlocal 只能修改**直接外层函数(enclosing scope)中定义的变量**,不能跳过一层去改更外层的,也不能修改全局变量或局部变量本身。如果外层没有同名变量,Python 会报 SyntaxError: no binding for nonlocal 'x' found。
- 外层函数必须已定义该变量(哪怕只是声明未赋值,如
x = None),否则 nonlocal x 无效
- 不能用于模块级(global)变量:想改全局变量请用
global
- 不能用于类方法内部试图修改外层函数变量——类定义不是函数作用域,不构成 enclosing scope
def outer():
x = 10
def inner():
nonlocal x # ✅ 合法:x 在 outer 中定义
x += 1
inner()
为什么赋值前引用会报 UnboundLocalError?
只要函数体内对某个变量用了 nonlocal 声明,Python 就把它当作“外层变量”,但**所有对该变量的读取都必须发生在写入之后**,否则触发 UnboundLocalError: local variable 'x' referenced before assignment——注意这个错误提示里仍叫它 “local variable”,容易误导。
- 这不是 bug,是 Python 作用域解析规则:一旦有
nonlocal x,该函数中所有 x 都绑定到外层,但首次读取时外层变量可能还没被当前函数写过
- 解决方式:确保首次使用前已有明确赋值,或先读再写(但读之前外层变量必须已初始化)
def outer():
x = 10
def inner():
nonlocal x
print(x) # ✅ 可以,outer 中 x 已初始化
x += 1
和 global 关键字混用时要注意什么?nonlocal 和 global 不能同时修饰同一个变量名,且它们互斥:一个变量要么属于 enclosing scope(用 nonlocal),要么属于 module scope(用 global),不能模糊。
- 如果外层函数没定义
x,但模块级有 x = 5,此时写 nonlocal x 会报错;必须用 global x
- 若嵌套三层:grandparent → parent → child,child 想改 parent 的
x,写 nonlocal x 即可;想改 grandparent 的,不行——nonlocal 不支持跨层,只能直连上一级
x = 100 # module level
def f():
x = 20
def g():
nonlocal x # ✅ 改 f 中的 x
# global x # ❌ 不能和 nonlocal 同时存在,也不该在这里用
x *= 2
实际调试时怎么快速定位 nonlocal 失效?
最常见失效不是语法错,而是**变量名拼写不一致**或**外层变量被重新赋值覆盖了作用域链**。
- 检查内外层变量名是否完全一致(大小写、下划线)
- 留意外层函数是否在调用内层函数前,已执行过类似
x = some_new_value 覆盖原绑定
- 使用
print(locals()) 和 print([v for v in dir() if not v.startswith('_')]) 辅助确认当前作用域中变量来源
x = None),否则 nonlocal x 无效 global nonlocal 声明,Python 就把它当作“外层变量”,但**所有对该变量的读取都必须发生在写入之后**,否则触发 UnboundLocalError: local variable 'x' referenced before assignment——注意这个错误提示里仍叫它 “local variable”,容易误导。
- 这不是 bug,是 Python 作用域解析规则:一旦有
nonlocal x,该函数中所有x都绑定到外层,但首次读取时外层变量可能还没被当前函数写过 - 解决方式:确保首次使用前已有明确赋值,或先读再写(但读之前外层变量必须已初始化)
def outer():
x = 10
def inner():
nonlocal x
print(x) # ✅ 可以,outer 中 x 已初始化
x += 1
和 global 关键字混用时要注意什么?nonlocal 和 global 不能同时修饰同一个变量名,且它们互斥:一个变量要么属于 enclosing scope(用 nonlocal),要么属于 module scope(用 global),不能模糊。
- 如果外层函数没定义
x,但模块级有 x = 5,此时写 nonlocal x 会报错;必须用 global x
- 若嵌套三层:grandparent → parent → child,child 想改 parent 的
x,写 nonlocal x 即可;想改 grandparent 的,不行——nonlocal 不支持跨层,只能直连上一级
x = 100 # module level
def f():
x = 20
def g():
nonlocal x # ✅ 改 f 中的 x
# global x # ❌ 不能和 nonlocal 同时存在,也不该在这里用
x *= 2
实际调试时怎么快速定位 nonlocal 失效?
最常见失效不是语法错,而是**变量名拼写不一致**或**外层变量被重新赋值覆盖了作用域链**。
- 检查内外层变量名是否完全一致(大小写、下划线)
- 留意外层函数是否在调用内层函数前,已执行过类似
x = some_new_value 覆盖原绑定
- 使用
print(locals()) 和 print([v for v in dir() if not v.startswith('_')]) 辅助确认当前作用域中变量来源
x,但模块级有 x = 5,此时写 nonlocal x 会报错;必须用 global x x,写 nonlocal x 即可;想改 grandparent 的,不行——nonlocal 不支持跨层,只能直连上一级 - 检查内外层变量名是否完全一致(大小写、下划线)
- 留意外层函数是否在调用内层函数前,已执行过类似
x = some_new_value覆盖原绑定 - 使用
print(locals())和print([v for v in dir() if not v.startswith('_')])辅助确认当前作用域中变量来源
真正麻烦的是闭包变量被意外捕获又未声明 nonlocal——这时你改的其实是新局部变量,外层值纹丝不动,还查不出错。这种静默失败比报错更难发现。


















