当我尝试这段代码:

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期望变量是本地的,但在每种情况下都有阻止赋值的逻辑错误的情况。


当前回答

最好的例子是:

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

Python有点奇怪,因为它将所有内容保存在不同作用域的字典中。原来的a b c在最上面的作用域,所以在最上面的字典里。该函数有自己的字典。当你到达print(a)和print(b)语句时,字典中没有这个名字,所以Python查找列表并在全局字典中找到它们。

现在我们得到c+=1,当然,它等价于c=c+1。当Python扫描这一行时,它说:“啊哈,有一个名为c的变量,我将把它放入我的本地作用域字典中。”然后当它在赋值的右边寻找c的值时,它找到了它的局部变量c,它还没有值,因此抛出了错误。

上面提到的语句global c只是告诉解析器它使用全局作用域的c,因此不需要新的c。

它之所以说行中有问题是因为它在尝试生成代码之前有效地查找名称,因此在某种意义上,它认为它还没有真正地执行那一行。我认为这是一个可用性错误,但学习不要太认真地对待编译器的消息通常是一个很好的实践。

如果这能让我感到安慰的话,我花了大约一天的时间来研究这个问题,然后我发现了Guido写的一些关于解释一切的字典的东西。

更新,见评论:

它不会扫描两次代码,但它确实分两个阶段扫描代码:词法分析和解析。

考虑一下如何解析这行代码。词法分析器读取源文本并将其分解为词素(语法的“最小组件”)。所以当它碰到直线时

c+=1

它把它分解成

SYMBOL(c) OPERATOR(+=) DIGIT(1)

The parser eventually wants to make this into a parse tree and execute it, but since it's an assignment, before it does, it looks for the name c in the local dictionary, doesn't see it, and inserts it in the dictionary, marking it as uninitialized. In a fully compiled language, it would just go into the symbol table and wait for the parse, but since it WON'T have the luxury of a second pass, the lexer does a little extra work to make life easier later on. Only, then it sees the OPERATOR, sees that the rules say "if you have an operator += the left hand side must have been initialized" and says "whoops!"

这里的问题是,它还没有真正开始对行进行解析。这些都是实际解析前的准备工作,所以行计数器还没有执行到下一行。因此,当它发出错误信号时,它仍然认为它在上一行。

就像我说的,你可能会认为这是一个可用性漏洞,但实际上这是一件相当普遍的事情。有些编译器更诚实,会说“第XXX行附近有错误”,但这个编译器没有。

C +=1赋值C, python假设赋值的变量是本地的,但在这种情况下,它没有在本地声明。

使用全局或非本地关键字。

Nonlocal只在python 3中有效,所以如果你在使用python 2并且不想让你的变量为全局变量,你可以使用一个可变对象:

my_variables = { # a mutable object
    'c': 3
}

def test():
    my_variables['c'] +=1

test()

在下面n = num的情况下,n是一个局部变量,num是一个全局变量:

num = 10

def test():
  # ↓ Local variable
    n = num
       # ↑ Global variable
    print(n)
  
test()

因此,没有错误:

10

但是在下面的num = num的情况下,两边的num是局部变量,右边的num还没有定义:

num = 10

def test():
   # ↓ Local variable
    num = num
         # ↑ Local variable not defined yet
    print(num)
  
test()

所以,有下面的错误:

UnboundLocalError:赋值前引用的本地变量'num'

此外,即使删除num = 10,如下所示:

# num = 10 # Removed

def test():
   # ↓ Local variable
    num = num
         # ↑ Local variable not defined yet
    print(num)
  
test()

下面是同样的错误:

UnboundLocalError:赋值前引用的本地变量'num'

因此,为了解决上述错误,将global num放在num = num之前,如下所示:

num = 10

def test():
    global num # Here
    num = num 
    print(num)
  
test()

这样,上述误差就得到了解决,如下图所示:

10

或者,在num = num之前定义局部变量num = 5,如下所示:

num = 10

def test():
    num = 5 # Here
    num = num
    print(num)
  
test()

这样,上述误差就得到了解决,如下图所示:

5

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

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

global c

作为函数的第一行。

至于python 3,现在有了

nonlocal c

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