这个C/ c++代码在Python中的等效代码是什么?

void foo()
{
    static int counter = 0;
    counter++;
    printf("counter is %d\n", counter);
}

具体来说,如何在函数级别实现静态成员,而不是在类级别?将函数放入类中会改变什么吗?


当前回答

使用函数的属性作为静态变量有一些潜在的缺点:

每次要访问变量时,都必须写出函数的全名。 外部代码可以很容易地访问该变量并打乱值。

第二个问题的惯用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

但我不知道内置类型,清楚地传达的目的。

其他回答

在尝试了几种方法后,我最终使用了@warvariuc的答案的改进版本:

import types

def func(_static=types.SimpleNamespace(counter=0)):
    _static.counter += 1
    print(_static.counter)

许多人已经建议测试“hasattr”,但有一个更简单的答案:

def func():
    func.counter = getattr(func, 'counter', 0) + 1

没有try/except,没有测试hasattr,只有默认的getattr。

def staticvariables(**variables):
    def decorate(function):
        for variable in variables:
            setattr(function, variable, variables[variable])
        return function
    return decorate

@staticvariables(counter=0, bar=1)
def foo():
    print(foo.counter)
    print(foo.bar)

就像上面vincent的代码一样,这将被用作函数装饰器,静态变量必须以函数名作为前缀访问。这段代码的优点(尽管每个人都可以聪明地看出这一点)是你可以有多个静态变量,并以更常规的方式初始化它们。

其他答案已经演示了您应该如何做到这一点。这里有一种方法你不应该:

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 

默认值仅在函数第一次求值时初始化,而不是每次执行时初始化,因此可以使用列表或任何其他可变对象来存储静态值。

有点相反,但这应该是有效的:

def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter
foo.counter = 0

如果你想让计数器初始化代码在顶部而不是底部,你可以创建一个装饰器:

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

然后像这样使用代码:

@static_vars(counter=0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

它仍然需要你使用foo。前缀,不幸的是。

(图片来源:@ony)