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
...
当然,无论您现在在哪里使用bare
ctr,都要使用
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,而条件变量不是其中之一。