不明白为啥会发生 UnboundLocalError(关闭)[重复]

Posted

技术标签:

【中文标题】不明白为啥会发生 UnboundLocalError(关闭)[重复]【英文标题】:Don't understand why UnboundLocalError occurs (closure) [duplicate]不明白为什么会发生 UnboundLocalError(关闭)[重复] 【发布时间】:2021-12-12 06:22:16 【问题描述】:

我在这里做错了什么?

counter = 0

def increment():
  counter += 1

increment()

上面的代码抛出一个UnboundLocalError

【问题讨论】:

Python chatroom 正在讨论这个问题以及它当前标记为重复的问题。 这里的许多答案都说要使用global,尽管这样可行,但当存在其他选项时,通常建议使用可修改的全局变量。 @ZeroPiraeus 2012 年提出的问题不能与 2016 年提出的问题重复......而较新的问题是重复的。 @dsh That's not true. @juanpa.arrivillaga 虽然一般问题是 关闭 并更新非本地绑定。 UnboundLocalError 也可能发生在完全局部变量上,但它们是不同的问题(使用不同的解决方案)。 【参考方案1】:

Python 没有变量声明,所以它必须自己找出变量的scope。它通过一个简单的规则来做到这一点:如果在函数内部对变量进行了赋值,则该变量被视为本地变量。[1] 因此,行

counter += 1

隐式地使counter 成为increment() 的局部变量。但是,尝试执行此行将尝试在分配局部变量counter 之前读取它的值,从而导致UnboundLocalError[2]

如果counter 是一个全局变量,global 关键字会有所帮助。如果increment() 是局部函数,counter 是局部变量,则可以在 Python 3.x 中使用 nonlocal

【讨论】:

python 3 文档有一个faq page on why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value 通过unboundlocalerror-local-variable-l-referenced-before-assignment-python 我注意到了,我在文件顶部声明了一个变量,我可以在函数内部毫无问题地读取它,但是要写入我在顶部声明的变量该文件,我不得不使用全局。 更深入的解释:docs.python.org/3.3/reference/…。赋值不仅可以绑定名称,导入也可以,因此您还可以从使用无限制导入名称的语句中获得UnboundLocalError。示例:def foo(): bar = deepcopy('a':1); from copy import deepcopy; return bar,然后是 from copy import deepcopy; foo()。如果本地导入 from copy import deepcopy 被移除,则调用成功。【参考方案2】:

您需要使用global statement,以便修改全局变量计数器,而不是局部变量:

counter = 0

def increment():
  global counter
  counter += 1

increment()

如果定义 counter 的封闭范围不是全局范围,则在 Python 3.x 上,您可以使用 nonlocal statement。在 Python 2.x 上的相同情况下,您将无法重新分配给非本地名称 counter,因此您需要使 counter 可变并对其进行修改:

counter = [0]

def increment():
  counter[0] += 1

increment()
print counter[0]  # prints '1'

【讨论】:

【参考方案3】:

要回答主题行中的问题,* 是的,Python 中有闭包,但它们仅适用于函数内部,而且(在 Python 2.x 中)它们是只读的;您不能将名称重新绑定到不同的对象(尽管如果对象是可变的,您可以修改其内容)。在 Python 3.x 中,您可以使用 nonlocal 关键字来修改闭包变量。

def incrementer():
    counter = 0
    def increment():
        nonlocal counter
        counter += 1
        return counter
    return increment

increment = incrementer()

increment()   # 1
increment()   # 2

* 最初提出的关于 Python 中的闭包的问题。

【讨论】:

【参考方案4】:

您的代码抛出UnboundLocalError 的原因已经在其他答案中得到了很好的解释。

但在我看来,您正在尝试构建类似itertools.count() 的东西。

那么你为什么不试试看它是否适合你的情况:

>>> from itertools import count
>>> counter = count(0)
>>> counter
count(0)
>>> next(counter)
0
>>> counter
count(1)
>>> next(counter)
1
>>> counter
count(2)

【讨论】:

【参考方案5】:

Python 默认具有词法作用域,这意味着尽管封闭作用域可以访问其封闭作用域中的值,但它不能修改它们(除非它们使用 global 关键字声明为全局)。

闭包将 enclosure 环境中的值绑定到 local 环境中的名称。然后本地环境可以使用绑定的值,甚至将该名称重新分配给其他名称,但它不能修改封闭环境中的绑定。

在您的情况下,您试图将 counter 视为局部变量而不是绑定值。请注意,此代码绑定了在封闭环境中分配的x 的值,可以正常工作:

>>> x = 1

>>> def f():
>>>  return x

>>> f()
1

【讨论】:

【参考方案6】:

要修改函数内部的全局变量,必须使用 global 关键字。

当你尝试在没有线的情况下这样做时

global counter

在increment的定义内部,创建了一个名为counter的局部变量,以免你弄乱整个程序可能依赖的counter变量。

注意,修改变量时只需要使用global即可;您可以从增量中读取计数器,而无需全局语句。

【讨论】:

【参考方案7】:

试试这个

counter = 0

def increment():
  global counter
  counter += 1

increment()

【讨论】:

【参考方案8】:

Python 不是纯粹的词法作用域。

看到这个:Using global variables in a function

还有这个:https://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review

以上是关于不明白为啥会发生 UnboundLocalError(关闭)[重复]的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 URLSessionTask 完成后 indexPath 会发生变化?

我不明白为啥会这样编译

我不明白为啥会出现语法错误

我收到警报:这违反了 Facebook 平台政策 4.8,但不明白为啥?

我不明白为啥会抛出“调用未定义的方法 CI_Input()::event()”

仪器报告内存泄漏。不明白为啥