Why can't Python increment variable in closure?
错误消息是不言而喻的。我在这里提供了一些解决方案。使用函数属性(不常见,但效果很好)
对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