函數中靜態變量的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。

相關文章
相關標籤/搜索