这个C/ c++代码在Python中的等效代码是什么?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
具体来说,如何在函数级别实现静态成员,而不是在类级别?将函数放入类中会改变什么吗?
这个C/ c++代码在Python中的等效代码是什么?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
具体来说,如何在函数级别实现静态成员,而不是在类级别?将函数放入类中会改变什么吗?
当前回答
许多人已经建议测试“hasattr”,但有一个更简单的答案:
def func():
func.counter = getattr(func, 'counter', 0) + 1
没有try/except,没有测试hasattr,只有默认的getattr。
其他回答
使用函数的属性作为静态变量有一些潜在的缺点:
每次要访问变量时,都必须写出函数的全名。 外部代码可以很容易地访问该变量并打乱值。
第二个问题的惯用python可能会用前导下划线来命名变量,以表明它不应该被访问,同时在事后保持它的可访问性。
使用闭包
另一种选择是使用词法闭包的模式,python 3中的nonlocal关键字支持这种模式。
def make_counter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter
counter = make_counter()
遗憾的是,我不知道如何将这个解决方案封装到装饰器中。
使用内部状态参数
另一种选择可能是使用未记录的参数作为可变值容器。
def counter(*, _i=[0]):
_i[0] += 1
return _i[0]
这是可行的,因为默认参数是在定义函数时计算的,而不是在调用函数时计算的。
更清洁的方法可能是使用容器类型而不是列表,例如:
def counter(*, _i = Mutable(0)):
_i.value += 1
return _i.value
但我不知道内置类型,清楚地传达的目的。
Python没有静态变量,但你可以通过定义一个可调用的类对象,然后将其用作函数来伪装它。也可以看看这个答案。
class Foo(object):
# Class variable, shared by all instances of this class
counter = 0
def __call__(self):
Foo.counter += 1
print Foo.counter
# Create an object instance of class "Foo," called "foo"
foo = Foo()
# Make calls to the "__call__" method, via the object's name itself
foo() #prints 1
foo() #prints 2
foo() #prints 3
注意,__call__使类(对象)的实例可以通过自己的名称调用。这就是为什么上面调用foo()会调用类的__call__方法。从文档中可以看到:
任意类的实例都可以通过在类中定义__call__()方法来实现可调用。
另一个(不推荐!)对https://stackoverflow.com/a/279598/916373这样的可调用对象的扭曲,如果您不介意使用一个时髦的调用签名的话
class foo(object):
counter = 0;
@staticmethod
def __call__():
foo.counter += 1
print "counter is %i" % foo.counter
>>> foo()()
counter is 1
>>> foo()()
counter is 2
其他解决方案将计数器属性附加到函数,通常使用复杂的逻辑来处理初始化。这对于新代码是不合适的。
在Python 3中,正确的方法是使用非局部语句:
counter = 0
def foo():
nonlocal counter
counter += 1
print(f'counter is {counter}')
有关非局部语句的规范,请参阅PEP 3104。
如果计数器是模块私有的,则应该将其命名为_counter。
其他答案已经演示了您应该如何做到这一点。这里有一种方法你不应该:
>>> def foo(counter=[0]):
... counter[0] += 1
... print("Counter is %i." % counter[0]);
...
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>
默认值仅在函数第一次求值时初始化,而不是每次执行时初始化,因此可以使用列表或任何其他可变对象来存储静态值。