读/写 Python 闭包
闭包是一种非常有用的编程特性。它们让我们能够做一些聪明的事情,这些事情如果用其他方式实现,可能需要写很多代码。而且,闭包通常能让我们的代码更加优雅和清晰。在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 个回答
在Python 3.x中,使用nonlocal
可以解决这个问题。
你可以这样做,效果大致相同:
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
。)
总之,</抱怨>
来详细说一下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()
时,都会得到一个独立的计数器。其他的解决方案,特别是使用itertools
和yield
的方式,更符合Python的习惯用法。