Python在编译期即根据赋值语句标记变量为局部变量,导致未赋值前读取触发UnboundLocalError;global/nonlocal需提前声明作用域意图,而非事后补救;类方法中同名变量需用globals()或显式类名访问。

Python编译期就锁定变量作用域,不是运行时才判断
报错根本原因不在“有没有定义”,而在Python解释器看到函数里有=(或+=、-=等赋值操作)时,**立刻把该变量标记为局部变量**。之后只要在赋值前读它,哪怕只有一行print(x),就会触发UnboundLocalError: local variable 'x' referenced before assignment。
这个判定发生在函数被定义时(即编译阶段),和if条件是否为True、try块是否执行、甚至赋值语句是否真的运行过,完全无关。
-
if False: x = 1→ 整个函数里x仍被当作局部变量 -
try: ... except: x = 1→ 同样,x被锁死为局部 - 哪怕赋值语句在
return之后,也改变不了编译期的判定
global和nonlocal不是“修复”,而是提前声明意图
加global x或nonlocal x不是在出错后补救,而是在函数开头就告诉Python:“我要操作的x不属于当前作用域”。不写,就默认按LEGB规则找;写了,就跳过局部查找,直接去指定作用域读写。
- 外层是模块级变量(即通常说的“全局变量”)→ 必须用
global x - 外层是嵌套函数里的变量(非全局)→ 必须用
nonlocal x,用global会创建/修改全局变量,不是你想要的 -
nonlocal必须能向上找到对应变量名,否则报SyntaxError,不能“越层跳” - 即使写了
nonlocal x,外层函数也必须已定义x,否则运行时报NameError
类方法里同名变量更危险:global无效,globals()才管用
在类的方法中,如果模块顶层和类里都有同名变量(比如都叫count),写count = 10会定义局部变量;想读模块级count却先写了赋值,就必然报UnboundLocalError。这时global count只能绑定模块顶层的count,对类变量MyClass.count完全无效。
- 改类变量:用
MyClass.count = 42或self.__class__.count = 42 - 读/写模块级同名变量:必须用
globals()['count'],它绕过作用域链,直取模块字典 - 别依赖“没赋值就不会当局部变量”——只要语法上存在赋值,就已锁定
最常踩的坑:分支逻辑里变量未全覆盖赋值
这类错误看起来像逻辑缺陷,实则是作用域误判。例如:
def get_status(code):
if code == 200:
msg = "OK"
elif code == 404:
msg = "Not Found"
return msg # UnboundLocalError if code is 500
因为msg在某些路径下没被赋值,Python仍把它视为局部变量,但return msg在所有路径都执行,于是出错。
- 解决方式不是加
else,而是**提前初始化**:msg = None或msg = "" - 或者确保每个分支都赋值,包括
else或except - 不要指望“这段代码不会走到”,Python不看控制流,只看语法树
真正容易被忽略的是:这个错误从不告诉你哪行触发了“局部化”,只报“引用前未赋值”——而问题根源往往在几十行之后那个不起眼的xxx = ...。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











