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

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

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


当前回答

米格尔·安吉洛的自我重新定义解决方案甚至可以不需要任何装饰:

def fun(increment=1):
    global fun
    counter = 0
    def fun(increment=1):
        nonlocal counter
        counter += increment
        print(counter)
    fun(increment)

fun()    #=> 1
fun()    #=> 2
fun(10)  #=> 12

第二行必须进行调整,以获得有限的范围:

def outerfun():
    def innerfun(increment=1):
        nonlocal innerfun
        counter = 0
        def innerfun(increment=1):
            nonlocal counter
            counter += increment
            print(counter)
        innerfun(increment)

    innerfun()    #=> 1
    innerfun()    #=> 2
    innerfun(10)  #=> 12

outerfun()

装饰器的优点是,你不必额外注意你的施工范围。

其他回答

惯用的方法是使用类,类可以有属性。如果您需要实例不分离,请使用单例。

有许多方法可以将“静态”变量伪造或蒙骗到Python中(到目前为止没有提到的一种方法是使用可变的默认参数),但这不是Python的惯用方法。只需要使用一个类。

如果您的使用模式合适,也可以使用生成器。

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__()方法来实现可调用。

许多人已经建议测试“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

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

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

import types

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