嵌套函数作用域中的UnboundLocalError
我有这样的代码(简化版):
def outer():
ctr = 0
def inner():
ctr += 1
inner()
但是 ctr
出现了错误:
Traceback (most recent call last):
File "foo.py", line 9, in <module>
outer()
File "foo.py", line 7, in outer
inner()
File "foo.py", line 5, in inner
ctr += 1
UnboundLocalError: local variable 'ctr' referenced before assignment
我该怎么解决这个问题呢?我以为嵌套作用域可以让我这样做。我试过使用 'global',但还是不行。
3 个回答
如果把 ctr
这个变量放在 outer
之外(也就是放在全局范围内),或者放在其他的类/函数里,会怎么样呢?这样做的话,这个变量就可以被访问和修改了。
解释
每当在一个函数内部给一个变量赋值时,Python就会把这个变量当作该函数的一个局部变量。这意味着即使赋值的操作没有执行,只要在函数里有赋值的代码,Python就会认为这个变量是局部的。比如说,ctr += 1
这行代码包含了对ctr
的赋值,所以Python认为ctr
是inner
函数的局部变量。因此,它根本不会去查看在outer
函数中定义的ctr
的值。Python看到的情况大致是这样的:
def inner():
ctr = ctr + 1
大家应该都同意,这段代码会出错,因为ctr
在被定义之前就被访问了。
(想了解更多关于Python如何决定变量作用域的内容,可以查看官方文档或这个问题。)
解决方案(在Python 3中)
Python 3引入了nonlocal
语句,它的作用和global
语句类似,但可以让我们访问外层函数的变量(而不是全局变量)。只需在inner
函数的顶部添加nonlocal ctr
,问题就解决了:
def outer():
ctr = 0
def inner():
nonlocal ctr
ctr += 1
inner()
变通方法(在Python 2中)
由于Python 2中没有nonlocal
语句,我们需要想办法解决。这里有两种简单的变通方法:
去掉对
ctr
的所有赋值因为Python只把
ctr
当作局部变量是因为有赋值操作,所以如果我们去掉对ctr
的所有赋值,问题就会消失。但我们怎么能在不赋值的情况下改变这个变量的值呢?很简单:我们把这个变量放在一个可变对象里,比如列表。这样我们就可以修改这个列表,而不需要给ctr
赋值:def outer(): ctr = [0] def inner(): ctr[0] += 1 inner()
将
ctr
作为参数传递给inner
def outer(): ctr = 0 def inner(ctr): ctr += 1 return ctr ctr = inner(ctr)
如果你在使用Python 3,你可以用nonlocal
这个关键词来重新绑定一个非本地的名字,也就是说可以改变它的值。
def outer():
ctr = 0
def inner():
nonlocal ctr
ctr += 1
inner()
如果你在使用Python 2,因为没有nonlocal
这个关键词,你需要在不重新绑定名字的情况下进行加法操作(也就是把计数器作为某个名字的一个项目或者属性来使用,而不是直接作为一个名字)。比如:
...
ctr = [0]
def inner():
ctr[0] += 1
...
当然,在其他地方使用ctr
的时候,要用ctr[0]
来代替直接使用ctr
。