Python Error - UnboundLocalError: local variable 'xxx' referenced before assignment
1 写在前面
UnboundLocalError 产生的原因很简单,就是后面「错误提示语句」的直译 “局部变量使用前未初始化”。但由于我们对 Python 语法的一些细节不熟悉,也由于 Python 语法过于灵活,可能明知这个原因也分析不出自己的代码哪里有问题。所以这里总结一下这个问题可能的原因,以及涉及的 Python 语法知识。
① 局部变量 & 全局变量相关的原因
语法知识:局部变量(主要指函数内的局部变量),与全局变量重名时,Python 解释器什么时候认定其为重名局部变量(语法允许),什么时候认定其为全局变量
-
重名变量,在函数内有全局声明时,当然为全局变量
-
除此之外,当重名变量在函数内存在(可以不是在函数内第一次出现时)赋值语句(包括
+=
语句)时,它被认定为局部变量,否则被认为是全局变量。
插说一句,这个语法规则很容易理解。因为当出现赋值语句时,说明重名变量在函数内的值已经与函数外的值完全无关,重新赋了个新值,所以完全没有必要视之为全局变量。
这就导致,当 函数内的、与函数外全局变量重名的变量,第一次在函数中出现不是赋值语句,而后面又在函数中出现了它的赋值语句 时,Python 解释器会报 “UnboundLocalError” 错误。这种情况的产生又分两种原因:
- 重名变量是全局变量,忘记对其用
global
关键字声明就使用(这种情况,如果后面没有赋值语句也无妨,解释器也会认为它是全局变量),但后面又出现了赋值语句,使 Python 解释器误解析为局部变量 - 重名变量是局部变量,忘记初始化了
修改方法不言而喻,是全局变量则用 global 声明,否则在第一次使用前初始化为期望的值。
② 函数中用到了判断语句,而局部变量的初始化语句放在了某条分支下
这会导致,在其他分支情况下,在判断语句外使用此变量时它并未初始化,解释器报错 “UnboundLocalError”。
修改方法就是把该初始化语句放到判断语句前面。
如果到这里可以明确自己所遇到的 “UnboundLocalError” 产生的原因,就不必看下面案例的模拟代码了;如果还不清楚,请继续向下看。
2 模拟代码
场景一 局部变量和全局变量重名
这种情况其实不太容易出现,因为很多IDE会直接用红波浪线警告。比如下面两种情况:
xxx = 1
def run():
# += 运算,先使用(+)再赋值(=)
xxx += 2 # xxx被认为是局部变量,且初始化前就用它加2,报错UnboundLocalError
run()
x = []
def run():
x.append(2)
x = [3,4] # 先使用,再赋值,报错UnboundLocalError
run()
修改方法正如开头所讲,要么在函数内用 global
关键字将变量声明为全局变量,要么把变量的赋值语句放在它被调用之前。
场景二 局部变量初始化语句放在判断语句某个分支中
今日执行 Python 代码遇到 ‘UnboundLocalError’ 时的场景就是如此,从中抽取出了下面的核心模拟代码:
def func(flag=True):
# xxx = 3 # 正确的做法
if flag:
xxx = 3
...
return xxx
# func() # 未报错
func(False) # 报错 UnboundLocalError
这段代码,使用默认参数时可正常运行;当修改参数为 False 时,会运行报错 “UnboundLocalError”。
从抽取出的核心代码中可以很容易看出,参数 flag = False
时,变量 xxx
没有初始化就被 return xxx
语句调用。这也正是 ERROR 产生的原因,即 使用了未初始化的变量。