无法访问方法内的全局变量

时间:2017-04-16 13:00:11

标签: python python-3.x

我有一个问题,当我尝试在方法中使用全局变量时,会产生错误(“赋值前引用的局部变量'b')。 当变量是列表的元素时,为什么不是这种情况?

这很好用:

a = [1]
def a_add():
    a[0] += 1

a_add()
print(a)

但这不是:

b = 1
def b_add():
    b += 1

b_add()
print(b)

3 个答案:

答案 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;