为什么会出现此 UnboundLocalError (closure)?[复制]

Why does this UnboundLocalError occur (closure)? [duplicate]

提问人:Randomblue 提问时间:2/14/2012 最后编辑:Peter MortensenRandomblue 更新时间:8/9/2022 访问量:292817

问:

我在这里做错了什么?

counter = 0

def increment():
  counter += 1

increment()

上面的代码抛出一个 .UnboundLocalError

Python 范围 闭包全 局变量

评论

1赞 Zero Piraeus 1/3/2017
这个问题和它目前标记为重复的问题正在 Python 聊天室中讨论。
5赞 PM 2Ring 1/3/2017
这里的许多答案都说使用 ,尽管这有效,但当存在其他选项时,通常建议使用可修改的全局变量。global
20赞 dsh 2/15/2017
@ZeroPiraeus 2012 年提出的问题不能与 2016 年提出的问题重复......相反,较新的是重复的。
10赞 Zero Piraeus 2/15/2017
@dsh 事实并非如此
0赞 Masklinn 3/9/2020
@juanpa.arrivillaga,但一般问题是关闭并更新非本地绑定。完全局部变量也可能发生 UnboundLocalError,但它们是不同的问题(使用不同的解决方案)。

答:

3赞 chucksmash 2/14/2012 #1

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

当您尝试在没有线的情况下执行此操作时

global counter

在增量的定义中,创建了一个名为 counter 的局部变量,以防止您弄乱整个程序可能依赖的计数器变量。

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

95赞 Andrew Clark 2/14/2012 #2

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

counter = 0

def increment():
  global counter
  counter += 1

increment()

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

counter = [0]

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

increment()
print counter[0]  # prints '1'
1赞 Lostsoul 2/14/2012 #3

试试这个:

counter = 0

def increment():
  global counter
  counter += 1

increment()
-2赞 Marcin 2/14/2012 #4

Python 不是纯粹的词法范围。

请参阅在函数中使用全局变量

以及 Python 变量范围说明

219赞 Sven Marnach 2/14/2012 #5

Python 没有变量声明,因此它必须自己弄清楚变量的范围。它通过一个简单的规则来实现:如果函数内部有一个变量赋值,则该变量被视为局部变量。[1] 因此,该行

counter += 1

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

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

评论

1赞 mouckatron 2/6/2018
一个让我大吃一惊的说明是,我在文件顶部声明了一个变量,我可以毫无问题地在函数中读取该变量,但是要写入我在文件顶部声明的变量,我必须使用 global。
0赞 Yibo Yang 6/28/2018
更深入的解释:docs.python.org/3.3/reference/....赋值不仅可以绑定名称,导入也可以绑定,因此您还可以从使用无界导入名称的语句中获取。示例:,则 。如果删除本地导入,则调用成功。UnboundLocalErrordef foo(): bar = deepcopy({'a':1}); from copy import deepcopy; return barfrom copy import deepcopy; foo()from copy import deepcopy
5赞 Chris Taylor 2/14/2012 #6

默认情况下,Python 具有词法范围,这意味着尽管封闭的范围可以访问其封闭范围中的值,但它无法修改它们(除非它们使用 global 关键字声明为 global)。

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

在您的情况下,您尝试将其视为局部变量而不是绑定值。请注意,此代码绑定了封闭环境中 assigned 的值,工作正常:counterx

>>> x = 1

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

>>> f()
1
30赞 kindall 2/14/2012 #7

要回答主题行中的问题,* 是的,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 闭包的问题。

7赞 Rik Poggi 2/14/2012 #8

你的代码抛出一个的原因已经在其他答案中得到了很好的解释。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)