簡體   English   中英

Python類方法中的類C靜態變量

[英]C-like Static Variable inside a Python class method

經過20年的C ++經驗,我努力學習Python。

現在,我想擁有一個方法(類中的函數),該方法具有自己的“靜態”變量,而不是靜態變量。

偽代碼示例可能可以更好地說明我想要的內容。

class dummy:
    @staticmethod
    def foo():
        foo.counter += 1
        print "You have called me {} times.".format(foo.counter)
    foo.counter = 0

注意1:我只是為了簡單起見使用@staticmethod ,但這無關緊要。

注2:這崩潰與AttributeError: 'staticmethod' object has no attribute 'counter'但是正如我上面所說,這是一個偽代碼,用於闡明我的目標。

我已經知道這課堂之外有效

def foo():
    foo.counter += 1
    print "You have called me {} times.".format(foo.counter)
foo.counter = 0

但是,相同的技巧似乎不適用於成員函數。

最后一分鍾的信息,我只能使用Python 2.7(不是我的選擇)。

是否有任何合法且可靠的方法來使作用域限於成員函數作用域的持久變量(或常數)?

一些相關鏈接

提前致謝。

不,那里沒有。 您已經找到了Python版本:類dummy開發的最高霸主您只能在函數foo訪問的類變量。

如果可以幫助您了解這樣做的原理,則可以從此處開始。 我希望您已經經歷了很多事情; 但是,此答案為Python提供了更多細節,以實現您所需的更多Pythonic方式。

正如@Prune已經提到的那樣,沒有真正的方法。

但是,如果您希望方法內的靜態變量僅可用於其所屬的對象(據我所知,就象在C ++中一樣),則應在構造函數中定義它或將其定義為帶有非靜態方法:

from __future__ import print_function

class dummy:
    def __init__(self, counter=0):
        self._foo_counter = 0

    def foo(self):
        self._foo_counter += 1
        print("You have called me {} times.".format(self._foo_counter))

要么:

class dummy:
    def foo(self):
        self._foo_counter += 1
        print("You have called me {} times.".format(self._foo_counter))

    _foo_counter = 0

這樣,運行:

x = dummy()
for _ in range(4):
    x.foo()

y = dummy()
for _ in range(4):
    y.foo()

結果是:

You have called me 1 times.
You have called me 2 times.
You have called me 3 times.
You have called me 4 times.
You have called me 1 times.
You have called me 2 times.
You have called me 3 times.
You have called me 4 times.

請注意,這兩個版本的行為方式並不完全相同。 當直接在類中定義_foo_counter時,您將可以同時訪問對象( self._foo_counter )和類本身( dummy._foo_counter )的_foo_counter變量。 對於每次使用該類, dummy._foo_counter都是靜態的,並且將在該類的多個實例之間(因此在多個對象之間)保持不變。 如果在dummy.foo()上使用@staticmethod裝飾器,這也是您可以訪問的唯一變量:

class dummy:
    @staticmethod
    def foo():
        dummy._foo_counter += 1
        print("You have called me {} times.".format(dummy._foo_counter))

    _foo_counter = 0

在這里, self_foo_counter將無法訪問,並且您唯一的選擇是使用類范圍的變量dummy._foo_counter (如前所述,您也可以將其與未使用@staticmethod裝飾的方法一起使用)。

這樣再次運行:

x = dummy()
for _ in range(4):
    x.foo()

y = dummy()
for _ in range(4):
    y.foo()

結果是:

You have called me 1 times.
You have called me 2 times.
You have called me 3 times.
You have called me 4 times.
You have called me 5 times.
You have called me 6 times.
You have called me 7 times.
You have called me 8 times.

實現此目的的一種方法是將變量隱藏在一個閉包中,因此對於您的目的而言,它實際上是靜態的。 不幸的是,Python的2不支持nonlocal關鍵字,所以我們有一個對象來包裝我們的變量的值(除非你只意味着引用發生變異的變量(即分配給該方法的變量):

In [7]: class _Nonlocal:
   ...:     def __init__(self, value):
   ...:         self.counter = value
   ...:
   ...: def foo_maker():
   ...:     nonlocal = _Nonlocal(0)
   ...:     def foo(self):
   ...:         nonlocal.counter += 1
   ...:         print "You have called me {} times.".format(nonlocal.counter)
   ...:     return foo
   ...:

In [8]: class Dummy(object): #you should always inherit from object explicitely in python 2
   ...:     foo = foo_maker()
   ...:

In [9]: dummy = Dummy()

In [10]: dummy.foo()
You have called me 1 times.

In [11]: dummy.foo()
You have called me 2 times.

當然,只是為了避免使用實例變量,這是很多麻煩的事情。 也許最好的解決方案是使您的方法成為自定義對象,並且您可以實現描述符協議以使其可作為方法調用,並且如果您願意,它將用作實例方法:

In [35]: import types
    ...:
    ...: class Foo(object):
    ...:     def __init__(this):
    ...:         this.counter = 0
    ...:     def __call__(this, self):
    ...:         this.counter += 1
    ...:         print "You have called me {} times.".format(this.counter)
    ...:         print "here is some instance state, self.bar: {}".format(self.bar)
    ...:     def __get__(this, obj, objtype=None):
    ...:         "Simulate func_descr_get() in Objects/funcobject.c"
    ...:         if obj is None:
    ...:             return this
    ...:         return types.MethodType(this, obj)
    ...:

In [36]: class Dummy(object): #you should always inherit from object explicitely in python 2
    ...:     foo = Foo()
    ...:     def __init__(self):
    ...:         self.bar = 42
    ...:

In [37]: dummy = Dummy()

In [38]: dummy.foo()
You have called me 1 times.
here is some instance state, self.bar: 42

In [39]: dummy.bar = 99

In [40]: dummy.foo()
You have called me 2 times.
here is some instance state, self.bar: 99

所有這些都是高度不規則的,並且會使習慣於python約定的其他人感到困惑,盡管我希望您看到,Python數據模型提供了許多自定義功能。

注意,我已經使用this作為第一個參數的名稱,以避免與混亂self實際上將來自該對象Foo獲得的綁定到一種方法。

我再次重申,我永遠不會這樣做。 如果您的函數需要維護狀態,則可以使用實例變量,或者使用生成器,並且可以將其用作迭代器。

使用可變類型作為函數關鍵字參數的默認值可能是最簡單的方法:

class Dummy:

    @staticmethod
    def foo(_counter=[0]):   # here using a list, but you could use a dictionary, or a deque
        _counter[0] += 1
        print "You have called me {} times.".format(_counter[0])

理由是此變量僅初始化一次; 它的最新價值保留在形成的封蓋中。

我已經在舊帖子中發布了此帖子,但沒人注意到它

由於我對靜態變量有不同的慣用目標,因此我想介紹以下內容:在一個函數中,我只想使用計算值初始化一次變量,而這可能會有點昂貴。 因為我喜歡出色的寫作,並且是一名老的C風格程序員。 我試圖定義類似宏的文字:

def  Foo () :
   StaticVar( Foo, ‘Var’, CalculateStatic())
   StaticVar( Foo, ‘Step’, CalculateStep())
   Foo.Var += Foo.Step
   print(‘Value of Var : ‘, Foo.Var)

然后,我這樣寫了“ StaticVar”:

  def StaticVar(Cls, Var, StaticVal) :
     if not hasattr(Cls, Var) :
        setattr(Cls, Var, StaticVal)

我什至可以用Python編寫更好的代碼:

def StaticVars(Cls, **Vars) :
    for Var, StaticVal in Vars.items() :
        if not hasattr(Cls, Var) :
            setattr(Cls, Var, StaticVal)

  def  Foo () :
      StaticVars( Foo, Var = CalculateStatic(),Step= CalculateStep()))
      Foo.Var += Foo. Step
      print(‘Value of Var : ‘, Foo.Var)

當然,這是編寫代碼的一種好方法,但是我的目標(僅一次調用初始化函數)沒有得到滿足(只需在初始化函數中添加打印以查看它經常被調用)! 事實是,在函數調用中,甚至在調用函數之前都會評估參數值。

def CalculateStatic() :
    print("Costly Initialization")
    return 0

def CalculateStep() :
    return 2

def Test() :
    Foo()
    Foo()
    Foo()

>>> Test()
Costly Initialization
Value of Var : 2
Costly Initialization
Value of Var : 4
Costly Initialization
Value of Var : 6

為了實現我的目標,我寧願這樣寫:

def  Foo () :
    if not hasattr(Foo, ‘Var’) :
        setattr ( Foo, ‘Var’, CalculateStatic())
        setattr ( Foo, ‘Step’, CalculateStep())

    Foo.Var += Foo. Step
    print(‘Value of Var : ‘, Foo.Var)

>>> Test()
Costly Initialization
Value of Var : 2
Value of Var : 4
Value of Var : 6

它可能像這樣“寫得很好”(我用下划線表示“ private == static”):

def StaticVars(Cls, **Vars) :
    for Var, StaticVal in Vars.items() :
        setattr(Cls, Var, StaticVal)

def  Foo () :
    _ = Foo
    try :
        __ = _.Var
    except AttributeError : # The above code could only generate AttributeError Exception
                    # the following code is executed only once
        StaticDefVars(_, Var= CalculateStatic(), Step = CalculateStep())

    _.Var += _. Step
    print(‘Value of Var : ‘, Foo.Var)

必須注意不要將“計算代碼”放在“ try”子句中,這可能會產生額外的“ AttributeError”異常。

當然,如果Python具有“ Marcro預處理”功能,那就更好了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM