函数中静态变量的Python等效项是什么?

此C / C ++代码的惯用Python等效项是什么? python

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

具体来讲,如何在函数级别(而不是类级别)实现静态成员? 并将函数放入类中是否会发生任何变化? app


#1楼

我我的更喜欢如下装饰器。 给每一个人本身。 ide

def staticize(name, factory):
    """Makes a pseudo-static variable in calling function.

    If name `name` exists in calling function, return it. 
    Otherwise, saves return value of `factory()` in 
    name `name` of calling function and return it.

    :param name: name to use to store static object 
    in calling function
    :type name: String
    :param factory: used to initialize name `name` 
    in calling function
    :type factory: function
    :rtype: `type(factory())`

    >>> def steveholt(z):
    ...     a = staticize('a', list)
    ...     a.append(z)
    >>> steveholt.a
    Traceback (most recent call last):
    ...
    AttributeError: 'function' object has no attribute 'a'
    >>> steveholt(1)
    >>> steveholt.a
    [1]
    >>> steveholt('a')
    >>> steveholt.a
    [1, 'a']
    >>> steveholt.a = []
    >>> steveholt.a
    []
    >>> steveholt('zzz')
    >>> steveholt.a
    ['zzz']

    """
    from inspect import stack
    # get scope enclosing calling function
    calling_fn_scope = stack()[2][0]
    # get calling function
    calling_fn_name = stack()[1][3]
    calling_fn = calling_fn_scope.f_locals[calling_fn_name]
    if not hasattr(calling_fn, name):
        setattr(calling_fn, name, factory())
    return getattr(calling_fn, name)

#2楼

这是一个彻底封装的版本,不须要外部初始化调用: 函数

def fn():
    fn.counter=vars(fn).setdefault('counter',-1)
    fn.counter+=1
    print (fn.counter)

在Python中,函数是对象,咱们能够经过特殊属性__dict__简单地向其添加或修改为员变量。 内置的vars()返回特殊属性__dict__测试

编辑:请注意,与替代的try:except AttributeError答案不一样,经过这种方法,变量将始终为初始化后的代码逻辑作好准备。 我认为如下方法的try:except AttributeError替代方法将减小DRY和/或具备笨拙的流程: ui

def Fibonacci(n):
   if n<2: return n
   Fibonacci.memo=vars(Fibonacci).setdefault('memo',{}) # use static variable to hold a results cache
   return Fibonacci.memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2)) # lookup result in cache, if not available then calculate and store it

EDIT2:仅当从多个位置调用该函数时,才建议使用上述方法。 相反,若是只在一个地方调用该函数,则最好使用nonlocalspa

def TheOnlyPlaceStaticFunctionIsCalled():
    memo={}
    def Fibonacci(n):
       nonlocal memo  # required in Python3. Python2 can see memo
       if n<2: return n
       return memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2))
    ...
    print (Fibonacci(200))
    ...

#3楼

还能够考虑: code

def foo():
    try:
        foo.counter += 1
    except AttributeError:
        foo.counter = 1

推理: 对象

  • 大量的pythonic( ask for forgiveness not permission
  • 使用异常(仅抛出一次)而不是if分支(认为StopIteration异常)

#4楼

这个问题的提示下,我能够提出另外一种选择,它可能会更好用,而且对于方法和函数来讲都同样: ci

@static_var2('seed',0)
def funccounter(statics, add=1):
    statics.seed += add
    return statics.seed

print funccounter()       #1
print funccounter(add=2)  #3
print funccounter()       #4

class ACircle(object):
    @static_var2('seed',0)
    def counter(statics, self, add=1):
        statics.seed += add
        return statics.seed

c = ACircle()
print c.counter()      #1
print c.counter(add=2) #3
print c.counter()      #4
d = ACircle()
print d.counter()      #5
print d.counter(add=2) #7
print d.counter()      #8    

若是您喜欢这种用法,请执行如下操做:

class StaticMan(object):
    def __init__(self):
        self.__dict__['_d'] = {}

    def __getattr__(self, name):
        return self.__dict__['_d'][name]
    def __getitem__(self, name):
        return self.__dict__['_d'][name]
    def __setattr__(self, name, val):
        self.__dict__['_d'][name] = val
    def __setitem__(self, name, val):
        self.__dict__['_d'][name] = val

def static_var2(name, val):
    def decorator(original):
        if not hasattr(original, ':staticman'):    
            def wrapped(*args, **kwargs):
                return original(getattr(wrapped, ':staticman'), *args, **kwargs)
            setattr(wrapped, ':staticman', StaticMan())
            f = wrapped
        else:
            f = original #already wrapped

        getattr(f, ':staticman')[name] = val
        return f
    return decorator

#5楼

不少人已经建议测试“ hasattr”,可是答案很简单:

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

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

相关文章
相关标签/搜索