当我尝试这段代码:

a, b, c = (1, 2, 3)

def test():
    print(a)
    print(b)
    print(c)
    c += 1
test()

我从打印(c)行得到一个错误,它说:

UnboundLocalError: local variable 'c' referenced before assignment

在Python的新版本中,或者

UnboundLocalError: 'c' not assigned

在一些老版本中。

如果注释掉c += 1,两次打印都成功。

我不明白:如果c不行,为什么打印a和b可以?c += 1是如何导致print(c)失败的,即使它出现在代码的后面?

赋值c += 1似乎创建了一个局部变量c,它优先于全局变量c。但是一个变量如何在它存在之前“窃取”作用域呢?为什么c是局部的?


请参见在函数中使用全局变量,了解如何从函数中重新分配全局变量的问题,以及是否可以在python中修改位于外部(封闭)但不是全局范围的变量?用于从封闭函数(闭包)重新赋值。

参见为什么不需要'global'关键字来访问全局变量?对于OP预期错误但没有得到错误的情况,从简单地访问一个没有global关键字的全局变量。

参见如何在Python中“解除绑定”名称?什么代码可以导致“UnboundLocalError”?对于OP期望变量是本地的,但在每种情况下都有阻止赋值的逻辑错误的情况。


当前回答

这不是对你的问题的直接回答,但它是密切相关的,因为这是由增广赋值和函数作用域之间的关系引起的另一个问题。

在大多数情况下,你倾向于认为增广赋值(a += b)与简单赋值(a = a + b)完全等价。不过,在一个角落的情况下,这可能会遇到一些麻烦。让我解释一下:

Python简单赋值的工作方式意味着,如果a被传递给一个函数(如func(a);注意Python总是引用传递),那么a = a + b将不会修改传入的a。相反,它只会修改指向a的局部指针。

但如果你使用a += b,那么它有时会实现为:

a = a + b

或者有时(如果方法存在)为:

a.__iadd__(b)

在第一种情况下(只要a没有声明为全局的),在局部作用域之外没有副作用,因为对a的赋值只是一个指针更新。

在第二种情况下,a实际上会修改自己,因此所有对a的引用都指向修改后的版本。下面的代码演示了这一点:

def copy_on_write(a):
      a = a + a
def inplace_add(a):
      a += a
a = [1]
copy_on_write(a)
print a # [1]
inplace_add(a)
print a # [1, 1]
b = 1
copy_on_write(b)
print b # [1]
inplace_add(b)
print b # 1

因此,诀窍是避免对函数参数进行扩展赋值(我尝试只对局部/循环变量使用它)。使用简单的赋值,就不会出现模棱两可的行为。

其他回答

Python对函数中的变量的处理方式不同,这取决于你是在函数内部还是函数外部为变量赋值。如果变量是在函数中赋值的,默认情况下它被视为局部变量。因此,当您取消注释该行时,您将尝试在为局部变量c赋值之前引用它。

如果你想让变量c引用在函数之前赋值的全局c = 3, put

global c

作为函数的第一行。

至于python 3,现在有了

nonlocal c

您可以使用它来引用最近的包含c变量的外围函数作用域。

看一看这个分解程序可能会清楚发生了什么:

>>> def f():
...    print a
...    print b
...    a = 1

>>> import dis
>>> dis.dis(f)

  2           0 LOAD_FAST                0 (a)
              3 PRINT_ITEM
              4 PRINT_NEWLINE

  3           5 LOAD_GLOBAL              0 (b)
              8 PRINT_ITEM
              9 PRINT_NEWLINE

  4          10 LOAD_CONST               1 (1)
             13 STORE_FAST               0 (a)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

As you can see, the bytecode for accessing a is LOAD_FAST, and for b, LOAD_GLOBAL. This is because the compiler has identified that a is assigned to within the function, and classified it as a local variable. The access mechanism for locals is fundamentally different for globals - they are statically assigned an offset in the frame's variables table, meaning lookup is a quick index, rather than the more expensive dict lookup as for globals. Because of this, Python is reading the print a line as "get the value of local variable 'a' held in slot 0, and print it", and when it detects that this variable is still uninitialised, raises an exception.

在初始化之后,通常在循环或条件块中对变量使用del关键字时,也会发生此问题。

最好的例子是:

bar = 42
def foo():
    print bar
    if False:
        bar = 0

当调用foo()时,这也会引发UnboundLocalError,尽管我们永远不会到达line bar=0,所以逻辑上不应该创建本地变量。

神秘之处在于“Python是一种解释型语言”,函数foo的声明被解释为一个单独的语句(即复合语句),它只是无声地解释它,并创建局部和全局作用域。因此bar在执行前在局部范围内被识别。

更多类似的例子请阅读这篇文章:http://blog.amir.rachum.com/blog/2013/07/09/python-common-newbie-mistakes-part-2/

这篇文章提供了对Python变量作用域的完整描述和分析:

访问类变量的最佳方法是直接通过类名访问

class Employee:
    counter=0

    def __init__(self):
        Employee.counter+=1