Python函数局部变量赋值前为何提前触发UnboundLocalError?
代码示例1:无异常的运行结果
l = [1, 2, 3, 4, 5] def foo(): l.append(10) foo()
这段代码可以正常运行,核心原因是函数foo内部没有对变量l做任何赋值操作,Python会将l识别为外层作用域的全局变量,正常执行列表追加逻辑。
代码示例2:预期和实际表现不符的运行结果
l = [1, 2, 3, 4, 5] def foo(): l.append(10) print(l) l += [10] foo()
按照直觉判断,异常应该在执行到赋值逻辑l += [10]这行才抛出,但实际运行时,在第一行首次引用l的l.append(10)位置就触发了UnboundLocalError。
背后的逻辑解释
- Python判定变量作用域的时机,是函数编译阶段,而非运行阶段。只要函数内部存在对某变量的赋值行为(包括
+=这类复合赋值,哪怕赋值代码永远不会被执行),解释器就会直接把这个变量标记为函数的局部变量,根本不会去外层作用域找同名的l变量。 - 这个标记过程在函数定义完成时就已经结束,根本不会等待程序运行到对应的赋值行才生效。
- 这和默认参数的求值逻辑有本质区别:默认参数是在函数定义时就完成实际值的绑定固定,而这里仅仅是做作用域的标记,完全不会读取变量的实际值,谈不上“定义时完成求值”。
- 当程序运行到
l.append(10)时,解释器已经认定l是局部变量,但此时这个局部变量还没有完成赋值绑定,因此直接抛出UnboundLocalError,和代码执行流有没有走到赋值行没有关系。
可以通过一个简单实验验证上述逻辑:哪怕把赋值代码放在永远不会触发的分支中:
l = [1, 2, 3] def foo(): l.append(10) if False: l += [10] foo()运行后依然会在
l.append(10)行抛出UnboundLocalError,足以证明作用域判定是编译期完成的,和运行时的代码走向无关。
内容的提问来源于stack exchange,提问作者kaktus_car
相关产品推荐
相关产品推荐

