我有一个问题,当我尝试在方法中使用全局变量时,会产生错误(“赋值前引用的局部变量'b')。 当变量是列表的元素时,为什么不是这种情况?
这很好用:
a = [1]
def a_add():
a[0] += 1
a_add()
print(a)
但这不是:
b = 1
def b_add():
b += 1
b_add()
print(b)
答案 0 :(得分:2)
当您尝试为b
分配内容时,Python会执行与本地人相关的LOAD_FAST
。在尝试使用global b
之前,您需要添加b
。
def b_add():
global b
b += 1
从另一个角度来看:
def b_add():
print(b)
Python代替加载与全局变量相关的LOAD_GLOBAL
。因此,当您执行a[0]
时,LOAD_GLOBAL
首先执行a
,然后存储该值。
答案 1 :(得分:2)
official FAQ page详细说明了此错误:
>>> x = 10
>>> def foo():
... print(x)
... x += 1
>>> foo()
Traceback (most recent call last):
...
UnboundLocalError: local variable 'x' referenced before assignment
这是因为当您对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量。由于foo中的最后一个语句为x分配了一个新值,编译器会将其识别为局部变量。因此,当较早的print(x)尝试打印未初始化的局部变量并导致错误时。
代码:
a = [1]
def a_add():
a[0] += 1
a_add()
print(a)
它只读取值并将值赋给global
数组的第一个插槽,因此没有问题。
答案 2 :(得分:0)
如果有人想深入研究CPython并查看导致其无效的确切原因,请查看指向CPypthon ceval.c源代码文件的链接。这是执行python字节码的代码。 如Vallentin所说,python会执行LOAD_FAST字节码指令来加载b(请参见下文)。
>>> x = 1
>>> def f():
... x += 1
...
>>> dis.dis(f)
2 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (1)
4 INPLACE_ADD
6 STORE_FAST 0 (x)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
在1228行的ceval.c文件中(截至2019年5月30日),相关代码如下:
case TARGET(LOAD_FAST): {
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
format_exc_check_arg(tstate, PyExc_UnboundLocalError,
UNBOUNDLOCAL_ERROR_MSG,
PyTuple_GetItem(co->co_varnames, oparg));
goto error;