当我尝试这段代码:

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


当前回答

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

class Employee:
    counter=0

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

其他回答

Python解释器将把函数作为一个完整的单元来读取。我认为它是分两次读取,一次是收集它的闭包(局部变量),然后再次将其转换为字节码。

我相信你已经意识到,在'='左边使用的任何名称都是隐式的局部变量。我不止一次因为改变一个变量对a +=的访问而被发现,它突然变成了一个不同的变量。

我还想指出的是,它与全局作用域并没有什么特别的关系。嵌套函数也有同样的行为。

当您尝试传统的全局变量语义时,Python有相当有趣的行为。我不记得细节了,但你可以读取在'global'范围内声明的变量的值,但如果你想修改它,你必须使用global关键字。试着把test()改成这样:

def test():
    global c
    print(a)
    print(b)
    print(c)    # (A)
    c+=1        # (B)

同样,你得到这个错误的原因是因为你也可以在函数中声明一个与“全局”同名的新变量,并且它将完全独立。解释器认为你试图在这个作用域中创建一个名为c的新变量,并在一个操作中修改它,这在Python中是不允许的,因为这个新的c没有初始化。

最好的例子是:

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变量作用域的完整描述和分析:

如果定义了与方法同名的变量,也可以得到此消息。

例如:

def teams():
    ...

def some_other_method():
    teams = teams()

解决方案是将方法teams()重命名为get_teams()。

因为它只在本地使用,所以Python消息相当具有误导性!

你最终会得到这样的结果:

def teams():
    ...

def some_other_method():
    teams = get_teams()

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

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

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

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

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

test()