为什么 Python 不能在闭包中增加变量?
我在这里提供了几个解决方案。
- 使用函数属性(不常见,但效果很好)
- 使用
nonlocal 的闭包(理想,但仅限 Python 3)
- 对可变对象使用闭包(Python 2 的惯用语)
- 在自定义对象上使用方法
- 通过实现
__call__直接调用对象的实例
在函数上使用属性。
在创建函数后手动为其设置计数器属性:
def foo():
foo.counter += 1
return foo.counter
foo.counter = 0
现在:
>>> foo()
1
>>> foo()
2
>>> foo()
3
或者您可以自动设置功能:
def foo():
if not hasattr(foo, 'counter'):
foo.counter = 0
foo.counter += 1
return foo.counter
同样:
>>> foo()
1
>>> foo()
2
>>> foo()
3
这些方法很简单,但并不常见,而且在你不在场的情况下查看你的代码的人不太可能很快理解。
您希望完成的更常见的方式取决于您的 Python 版本。
Python 3,使用带有nonlocal 的闭包
在 Python 3 中,您可以声明非本地:
def foo():
counter = 0
def bar():
nonlocal counter
counter += 1
print("bar", counter)
return bar
bar = foo()
它会增加
>>> bar()
bar 1
>>> bar()
bar 2
>>> bar()
bar 3
这可能是这个问题最惯用的解决方案。太糟糕了,它仅限于 Python 3。
非本地的 Python 2 解决方法:
您可以声明一个全局变量,然后对其进行递增,但这会使模块命名空间变得混乱。因此,避免声明全局变量的惯用解决方法是指向一个可变对象,该对象包含您希望递增的整数,这样您就不会尝试重新分配变量名称:
def foo():
counter = [0]
def bar():
counter[0] += 1
print("bar", counter)
return bar
bar = foo()
现在:
>>> bar()
('bar', [1])
>>> bar()
('bar', [2])
>>> bar()
('bar', [3])
我确实认为这比创建类来保存递增变量的建议要好。但要完整,让我们看看。
使用自定义对象
class Foo(object):
def __init__(self):
self._foo_call_count = 0
def foo(self):
self._foo_call_count += 1
print('Foo.foo', self._foo_call_count)
foo = Foo()
现在:
>>> foo.foo()
Foo.foo 1
>>> foo.foo()
Foo.foo 2
>>> foo.foo()
Foo.foo 3
甚至实现__call__:
class Foo2(object):
def __init__(self):
self._foo_call_count = 0
def __call__(self):
self._foo_call_count += 1
print('Foo', self._foo_call_count)
foo = Foo2()
现在:
>>> foo()
Foo 1
>>> foo()
Foo 2
>>> foo()
Foo 3