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

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

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


当前回答

使用装饰器和闭包

下面的装饰器可用于创建静态函数变量。它将声明的函数替换为函数本身的返回值。这意味着被修饰的函数必须返回一个函数。

def static_inner_self(func):
    return func()

然后在返回另一个带有捕获变量的函数的函数上使用decorator:

@static_inner_self
def foo():
    counter = 0
    def foo():
        nonlocal counter
        counter += 1
        print(f"counter is {counter}")
    return foo

nonlocal是必需的,否则Python认为计数器变量是一个局部变量而不是一个捕获变量。Python之所以如此,是因为变量赋值counter += 1。函数中的任何赋值都会使Python认为该变量是局部变量。

如果你没有在内部函数中为变量赋值,那么你可以忽略非局部语句,例如,在这个函数中,我用来缩进字符串的行,在这个函数中,Python可以推断出变量是非局部的:

@static_inner_self
def indent_lines():
    import re
    re_start_line = re.compile(r'^', flags=re.MULTILINE)
    def indent_lines(text, indent=2):
        return re_start_line.sub(" "*indent, text)
    return indent_lines

附注:有一个被删除的答案提出了同样的问题。我不知道作者为什么删掉它。 https://stackoverflow.com/a/23366737/195417

其他回答

可读性更强一点,但更冗长(Python的Zen:显式比隐式更好):

>>> def func(_static={'counter': 0}):
...     _static['counter'] += 1
...     print _static['counter']
...
>>> func()
1
>>> func()
2
>>>

请看这里,了解它是如何工作的。

另一个(不推荐!)对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

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

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()

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

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 myfunc():
  myfunc.counter += 1
  print myfunc.counter

# attribute must be initialized
myfunc.counter = 0

或者,如果你不想在函数外部设置变量,你可以使用hasattr()来避免AttributeError异常:

def myfunc():
  if not hasattr(myfunc, "counter"):
     myfunc.counter = 0  # it doesn't exist yet, so initialize it
  myfunc.counter += 1

无论如何,静态变量是相当罕见的,您应该为这个变量找到一个更好的位置,最有可能是在类中。