Python 具有嵌套函数作用域的UnboundLocalError
我有这样的代码(简化): 但是Python 具有嵌套函数作用域的UnboundLocalError,python,function,Python,Function,我有这样的代码(简化): 但是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' re
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
回溯(最近一次呼叫最后一次):
文件“foo.py”,第9行,在
外()
文件“foo.py”,第7行,在外部
内()
文件“foo.py”,第5行,在内部
ctr+=1
UnboundLocalError:赋值前引用的局部变量“ctr”
我怎样才能解决这个问题?我以为嵌套作用域会允许我这样做。我试过使用“global”,但仍然不起作用。来自
嵌套函数体中的代码可能
访问(但不重新绑定)本地
外部函数的变量,也是
称为嵌套的自由变量
功能
因此,您需要显式地将
ctr
传递到内部
。将ctr
声明在外部
(即在全局范围内)或任何其他类/函数之外如何?这将使变量可以访问和写入。如果您使用的是Python 3,则可以使用nonlocal
语句来启用非本地名称的重新绑定:
def outer():
ctr = 0
def inner():
nonlocal ctr
ctr += 1
inner()
如果您使用的是Python 2,它没有非本地
,那么您需要在不重新绑定barename的情况下执行递增(通过将计数器保留为某个barename的项或属性,而不是作为barename本身)。例如:
...
ctr = [0]
def inner():
ctr[0] += 1
...
当然,无论您现在在哪里使用barectr,都要使用ctr[0]
。解释
每当一个值被分配给函数中的一个变量时,python就会将该变量视为该函数的局部变量。(即使赋值是否执行也无关紧要——只要函数中存在赋值,被赋值的变量将被视为该函数的局部变量。)因为语句ctr+=1
包括对ctr
的赋值,python认为ctr
是内部函数的局部函数。因此,它甚至从未尝试查看在outer
中定义的ctr
变量的值。python看到的本质是:
def inner():
ctr = ctr + 1
我想我们都同意这个代码会导致错误,因为ctr
在定义之前就被访问了
(有关python如何决定变量范围的更多详细信息,请参见或。)
解决方案(在python 3中)
Python3引入了,其工作原理与global
语句非常相似,但允许我们访问周围函数的变量(而不是全局变量)。只需在内部
函数顶部添加非本地ctr
,问题就会消失:
def outer():
ctr = 0
def inner():
nonlocal ctr
ctr += 1
inner()
变通方法(在python 2中)
由于Python2中不存在非本地的语句,因此我们必须很巧妙。有两个简单的解决方法:
- 删除所有分配给
ctr
def outer():
ctr = 0
def inner(ctr):
ctr += 1
return ctr
ctr = inner(ctr)
由于python只考虑ctr
一个局部变量,因为该变量有赋值,因此如果我们删除对名称ctr
的所有赋值,问题就会消失。但是我们如何在不给变量赋值的情况下改变变量的值呢?简单:我们将变量包装在可变对象中,如列表。然后,我们可以修改该列表,而无需为名称ctr
赋值:
def outer():
ctr = [0]
def inner():
ctr[0] += 1
inner()
- 将
ctr
作为参数传递给internal
def outer():
ctr = 0
def inner(ctr):
ctr += 1
return ctr
ctr = inner(ctr)
我尝试过使用def keyfunc(x,k=ctr),然后使用k+=1。但这是行不通的,因为这样k就成为函数局部范围的一部分,并且不会更新外部变量ctr。更新ctr
需要在其自身范围内完成。您可以在调用keyfunc
之前或之后更新它。这是不可能的,因为它是sorted()的一个参数,由Python内部调用,而不是由我直接调用。我也不能把它放在lambda中,因为lambda只允许表达式afaik。@托马斯O:你可以使用functools.partial
来处理这个key=functools.partial(keyfunc,ctr)
这看起来太像一个“黑客”了。我将使用它,但它似乎是Python2.x的一个限制。我想我很快就会使用3.x了。Alex-谢谢-这个范围问题让2.x中的函数组合变得混乱;当我分配变量时,我正在疯狂地试图理解为什么会出现UnboundLocalError
。现在我明白了,这是有道理的。在C++中,除了在声明参数之前声明捕获,而不是在lambda的正文中,我们还用lambdas表示相同的概念。是的,但是仅仅为一个或两个函数使用全局变量有点混乱-最好将它们保留在局部范围内,这样你就知道为什么要首先将它们放在那里。我总是喜欢解释特定行为的答案,而不是仅仅放一段代码+1在内部函数的条件语句中使用外部变量时,也会发生您所解释的内部函数的局部作用域行为,而不仅仅是在变量赋值期间。@SeanFrancisN.balla不正确。唯一使变量成为局部变量的是a,而条件变量不是其中之一。