读/写 Python 闭包

39 投票
8 回答
5435 浏览
提问于 2025-04-15 17:40

闭包是一种非常有用的编程特性。它们让我们能够做一些聪明的事情,这些事情如果用其他方式实现,可能需要写很多代码。而且,闭包通常能让我们的代码更加优雅和清晰。在Python 2.x中,闭包里的变量名不能被重新绑定;也就是说,在一个函数内部,不能像这样 some_var = 'changed!' 去改变外部作用域的变量。我想知道这是为什么。有时候我希望创建一个可以重新绑定外部变量的闭包,但这并不可能。我明白在几乎所有情况下(如果不是全部的话),这种行为可以通过类来实现,但通常这样做不够简洁或优雅。为什么我不能用闭包来做到这一点呢?

下面是一个重新绑定的闭包示例:

def counter():
    count = 0
    def c():
        count += 1
        return count
    return c

这是你调用它时的当前行为:

>>> c()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in c
UnboundLocalError: local variable 'count' referenced before assignment

我希望它能这样做:

>>> c()
1
>>> c()
2
>>> c()
3

8 个回答

17

在Python 3.x中,使用nonlocal可以解决这个问题。

23

你可以这样做,效果大致相同:

class counter(object):
    def __init__(self, count=0):
        self.count = count
    def __call__(self):
        self.count += 1
        return self.count    

或者,有点小技巧:

def counter():
    count = [0]
    def incr(n):
        n[0] += 1
        return n[0]
    return lambda: incr(count)

我会选择第一个方案。

补充一下:这就是我没仔细看长篇大论的结果。

总之,Python的闭包功能比较有限,原因就是“因为Guido(Python的创始人)觉得这样好。”Python是在90年代初设计的,那时候面向对象编程(OO)非常流行。闭包在当时并不是人们特别想要的语言特性。随着一些函数式编程的概念,比如一等函数、闭包等逐渐流行,像Python这样的语言不得不把这些功能加上去,所以使用起来可能会有点别扭,因为这并不是语言最初设计的目的。

<抱怨关于Python作用域>

另外,我觉得Python(2.x)在作用域方面有些奇怪的想法,这影响了闭包的正常实现,其他方面也是如此。让我感到困扰的是,这样的代码:

new = [x for x in old]

会让我们在使用它的作用域中定义一个名为x的变量,而我认为这实际上是一个更小的作用域。(不过Python在一致性方面得了分,因为在for循环中做同样的事情也有相同的表现。避免这种情况的唯一方法是使用map。)

总之,</抱怨>

32

来详细说一下Ignacio的回答:

def counter():
    count = 0
    def c():
        nonlocal count
        count += 1
        return count
    return c

x = counter()
print([x(),x(),x()])

在Python 3中,这段代码会返回[1,2,3];每次调用counter()时,都会得到一个独立的计数器。其他的解决方案,特别是使用itertoolsyield的方式,更符合Python的习惯用法。

撰写回答