parameter - python static method global variable




靜態類變量可能嗎? (11)

類工廠python3.6中的靜態變量

對於任何使用python3.6以上的類工廠的人來說,使用nonlocal關鍵字將其添加到正在創建的類的作用域/上下文中,如下所示:

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world

是否有可能在Python中有靜態類變量或方法? 需要什麼語法才能做到這一點?


靜態和類方法

正如其他答案所指出的那樣,使用內置裝飾器可以輕鬆完成靜態和類方法:

class Test(object):

    # regular instance method:
    def MyMethod(self):
        pass

    # class method:
    @classmethod
    def MyClassMethod(klass):
        pass

    # static method:
    @staticmethod
    def MyStaticMethod():
        pass

像往常一樣, MyMethod()的第一個參數綁定到類實例對象。 相反, MyClassMethod()的第一個參數綁定到類對象本身 (例如,在這種情況下, Test )。 對於MyStaticMethod() ,沒有任何參數被綁定,並且根本沒有參數是可選的。

“靜態變量”

然而,實現“靜態變量”(好吧, 可變靜態變量,無論如何,如果這不是矛盾的話......)並不那麼直截了當。 正如millerdev 在他的回答中指出的那樣 ,問題是Python的類屬性並不是真正的“靜態變量”。 考慮:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

這是因為行xi = 12已將新的實例屬性i添加到x而不是更改Test class i屬性的值。

部分預期靜態變量行為,即多個實例之間的屬性同步(但與類本身同步;請參閱下面的“gotcha”),可以通過將class屬性轉換為屬性來實現:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

    @i.setter
    def i(self,val):
        type(self)._i = val

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    def set_i(self,val):
        type(self)._i = val

    i = property(get_i, set_i)

現在你可以這樣做:

x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i  # no error
assert x2.i == 50    # the property is synced

靜態變量現在將在所有類實例之間保持同步。

(注意:也就是說,除非一個類實例決定定義它自己的_i !版本,但如果有人決定去做那個,他們應該得到他們得到的東西,不是嗎?)

請注意,從技術上講, i仍然不是一個'靜態變量'; 它是一種property ,它是一種特殊類型的描述符。 但是, property行為現在等同於跨所有類實例同步的(可變)靜態變量。

不變的“靜態變量”

對於不可變的靜態變量行為,只需省略property設置器即可:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

現在嘗試設置實例i屬性將返回一個AttributeError

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

一個需要注意的問題

請注意,上述方法只適用於您的類的實例 - 它們在使用類本身時不起作用。 例如:

x = Test()
assert x.i == Test.i  # ERROR

# x.i and Test.i are two different objects:
type(Test.i)  # class 'property'
type(x.i)     # class 'int'

該行assert Test.i == xi會產生一個錯誤,因為Testxi屬性是兩個不同的對象。

許多人會發現這令人驚訝。 但是,它不應該。 如果我們回顧並檢查我們的Test類定義(第二個版本),我們注意到這一行:

    i = property(get_i) 

顯然, Test的成員i必須是一個property對象,它是property函數返回的對象的類型。

如果您發現上述混淆,您很可能仍然從其他語言(例如Java或c ++)的角度思考它。 您應該研究property對象,關於返回Python屬性的順序,描述符協議和方法解析順序(MRO)。

我為下面的'gotcha'提出了一個解決方案; 但是我會建議 - 非常努力 - 你不會嘗試做類似於以下的事情,直到 - 至少 - 你徹底明白為什麼assert Test.i = xi會導致錯誤。

REAL,ACTUAL靜態變量 - Test.i == xi

我只提供下面的(Python 3)解決方案,僅供參考。 我不贊同它是一個“好的解決方案”。 我懷疑是否真的需要在Python中模擬其他語言的靜態變量行為。 但是,不管它是否真的有用,下面都應該有助於進一步了解Python的工作原理。

更新:這個嘗試真的很糟糕 ; 如果你堅持要做這樣的事情(提示:請不要; Python是一種非常優雅的語言,並且讓它像另一種語言一樣行為就沒有必要),請用Ethan Furman的回答代替。

使用元類來模擬其他語言的靜態變量行為

元類是一個類的類。 Python中所有類的默認元類(也就是我相信Python 2.3中的“新樣式”類)是type 。 例如:

type(int)  # class 'type'
type(str)  # class 'type'
class Test(): pass
type(Test) # class 'type'

但是,您可以像這樣定義自己的元類:

class MyMeta(type): pass

並將其應用於您自己的類(Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

下面是我創建的一個元類,它嘗試模擬其他語言的“靜態變量”行為。 它的基本工作原理是將默認的getter,setter和deleter替換為檢查被請求的屬性是否為“靜態變量”的版本。

“靜態變量”的目錄存儲在StaticVarMeta.statics屬性中。 所有屬性請求最初都嘗試使用替代解析順序進行解析。 我稱之為“靜態分辨率訂單”或“SRO”。 這是通過在給定類(或其父類)的“靜態變量”集合中查找請求的屬性來完成的。 如果該屬性未出現在“SRO”中,則該類將回退到缺省屬性get / set / delete行為(即“MRO”)。

from functools import wraps

class StaticVarsMeta(type):
    '''A metaclass for creating classes that emulate the "static variable" behavior
    of other languages. I do not advise actually using this for anything!!!

    Behavior is intended to be similar to classes that use __slots__. However, "normal"
    attributes and __statics___ can coexist (unlike with __slots__). 

    Example usage: 

        class MyBaseClass(metaclass = StaticVarsMeta):
            __statics__ = {'a','b','c'}
            i = 0  # regular attribute
            a = 1  # static var defined (optional)

        class MyParentClass(MyBaseClass):
            __statics__ = {'d','e','f'}
            j = 2              # regular attribute
            d, e, f = 3, 4, 5  # Static vars
            a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)

        class MyChildClass(MyParentClass):
            __statics__ = {'a','b','c'}
            j = 2  # regular attribute (redefines j from MyParentClass)
            d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)
            a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''
    statics = {}
    def __new__(mcls, name, bases, namespace):
        # Get the class object
        cls = super().__new__(mcls, name, bases, namespace)
        # Establish the "statics resolution order"
        cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))

        # Replace class getter, setter, and deleter for instance attributes
        cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
        cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
        cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
        # Store the list of static variables for the class object
        # This list is permanent and cannot be changed, similar to __slots__
        try:
            mcls.statics[cls] = getattr(cls,'__statics__')
        except AttributeError:
            mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
        # Check and make sure the statics var names are strings
        if any(not isinstance(static,str) for static in mcls.statics[cls]):
            typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
            raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
        # Move any previously existing, not overridden statics to the static var parent class(es)
        if len(cls.__sro__) > 1:
            for attr,value in namespace.items():
                if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
                    for c in cls.__sro__[1:]:
                        if attr in StaticVarsMeta.statics[c]:
                            setattr(c,attr,value)
                            delattr(cls,attr)
        return cls
    def __inst_getattribute__(self, orig_getattribute):
        '''Replaces the class __getattribute__'''
        @wraps(orig_getattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                return StaticVarsMeta.__getstatic__(type(self),attr)
            else:
                return orig_getattribute(self, attr)
        return wrapper
    def __inst_setattr__(self, orig_setattribute):
        '''Replaces the class __setattr__'''
        @wraps(orig_setattribute)
        def wrapper(self, attr, value):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__setstatic__(type(self),attr, value)
            else:
                orig_setattribute(self, attr, value)
        return wrapper
    def __inst_delattr__(self, orig_delattribute):
        '''Replaces the class __delattr__'''
        @wraps(orig_delattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__delstatic__(type(self),attr)
            else:
                orig_delattribute(self, attr)
        return wrapper
    def __getstatic__(cls,attr):
        '''Static variable getter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    return getattr(c,attr)
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __setstatic__(cls,attr,value):
        '''Static variable setter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                setattr(c,attr,value)
                break
    def __delstatic__(cls,attr):
        '''Static variable deleter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    delattr(c,attr)
                    break
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __delattr__(cls,attr):
        '''Prevent __sro__ attribute from deletion'''
        if attr == '__sro__':
            raise AttributeError('readonly attribute')
        super().__delattr__(attr)
    def is_static(cls,attr):
        '''Returns True if an attribute is a static variable of any class in the __sro__'''
        if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
            return True
        return False

python中的靜態方法稱為classmethod s。 看看下面的代碼

class MyClass:

    def myInstanceMethod(self):
        print 'output from an instance method'

    @classmethod
    def myStaticMethod(cls):
        print 'output from a static method'

>>> MyClass.myInstanceMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method myInstanceMethod() must be called [...]

>>> MyClass.myStaticMethod()
output from a static method

請注意,當我們調用方法myInstanceMethod時 ,會出現錯誤。 這是因為它要求在該類的一個實例上調用該方法。 方法myStaticMethod被設置為使用decorator @classmethod的類方法。

只是為了踢和咯咯,我們可以通過傳入類的實例來調用類的myInstanceMethod ,如下所示:

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

你也可以使用元類強制一個類是靜態的。

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kw):
        raise StaticClassError("%s is a static class and cannot be initiated."
                                % cls)

class MyClass(StaticClass):
    a = 1
    b = 3

    @staticmethod
    def add(x, y):
        return x+y

然後,每當偶然你嘗試初始化MyClass你會得到一個StaticClassError。


您也可以將類變量隨時添加到類中

>>> class X:
...     pass
... 
>>> X.bar = 0
>>> x = X()
>>> x.bar
0
>>> x.foo
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: X instance has no attribute 'foo'
>>> X.foo = 1
>>> x.foo
1

而類實例可以改變類變量

class X:
  l = []
  def __init__(self):
    self.l.append(1)

print X().l
print X().l

>python test.py
[1]
[1, 1]

我個人會在需要靜態方法時使用classmethod。 主要是因為我把課堂作為論點。

class myObj(object):
   def myMethod(cls)
     ...
   myMethod = classmethod(myMethod) 

或使用裝飾器

class myObj(object):
   @classmethod
   def myMethod(cls)

對於靜態屬性..當你查找一些python定義的時候..變量總是可以改變的。 有兩種類型的可變和不可變的。此外,還有類屬性和實例屬性..沒有什麼真正像java和c + +的意義上的靜態屬性

為什麼在pythonic意義上使用靜態方法,如果它與類沒有任何關係! 如果我是你,我會使用classmethod或者定義獨立於類的方法。


有可能有static類變量,但可能不值得付出努力。

這是用Python 3編寫的一個概念驗證 - 如果任何確切的細節都是錯誤的,那麼可以調整代碼以便與static variable無關地匹配:

class Static:
    def __init__(self, value, doc=None):
        self.deleted = False
        self.value = value
        self.__doc__ = doc
    def __get__(self, inst, cls=None):
        if self.deleted:
            raise AttributeError('Attribute not set')
        return self.value
    def __set__(self, inst, value):
        self.deleted = False
        self.value = value
    def __delete__(self, inst):
        self.deleted = True

class StaticType(type):
    def __delattr__(cls, name):
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__delete__(name)
        else:
            super(StaticType, cls).__delattr__(name)
    def __getattribute__(cls, *args):
        obj = super(StaticType, cls).__getattribute__(*args)
        if isinstance(obj, Static):
            obj = obj.__get__(cls, cls.__class__)
        return obj
    def __setattr__(cls, name, val):
        # check if object already exists
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__set__(name, val)
        else:
            super(StaticType, cls).__setattr__(name, val)

並在使用中:

class MyStatic(metaclass=StaticType):
    """
    Testing static vars
    """
    a = Static(9)
    b = Static(12)
    c = 3

class YourStatic(MyStatic):
    d = Static('woo hoo')
    e = Static('doo wop')

和一些測試:

ms1 = MyStatic()
ms2 = MyStatic()
ms3 = MyStatic()
assert ms1.a == ms2.a == ms3.a == MyStatic.a
assert ms1.b == ms2.b == ms3.b == MyStatic.b
assert ms1.c == ms2.c == ms3.c == MyStatic.c
ms1.a = 77
assert ms1.a == ms2.a == ms3.a == MyStatic.a
ms2.b = 99
assert ms1.b == ms2.b == ms3.b == MyStatic.b
MyStatic.a = 101
assert ms1.a == ms2.a == ms3.a == MyStatic.a
MyStatic.b = 139
assert ms1.b == ms2.b == ms3.b == MyStatic.b
del MyStatic.b
for inst in (ms1, ms2, ms3):
    try:
        getattr(inst, 'b')
    except AttributeError:
        pass
    else:
        print('AttributeError not raised on %r' % attr)
ms1.c = 13
ms2.c = 17
ms3.c = 19
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19
MyStatic.c = 43
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19

ys1 = YourStatic()
ys2 = YourStatic()
ys3 = YourStatic()
MyStatic.b = 'burgler'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
assert ys1.d == ys2.d == ys3.d == YourStatic.d
assert ys1.e == ys2.e == ys3.e == YourStatic.e
ys1.a = 'blah'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
ys2.b = 'kelp'
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
ys1.d = 'fee'
assert ys1.d == ys2.d == ys3.d == YourStatic.d
ys2.e = 'fie'
assert ys1.e == ys2.e == ys3.e == YourStatic.e
MyStatic.a = 'aargh'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a

為了避免任何可能的混淆,我想對比靜態變量和不可變對象。

一些原始對像類型,如整數,浮點數,字符串和元組在Python中是不可變的。 這意味著如果它是前述對像類型之一,則由給定名稱引用的對像不能更改。 該名稱可以重新分配給不同的對象,但對象本身可能不會更改。

製作一個變量static通過禁止變量名稱指向任何對象,而不是它當前指向的對象來進一步進行。 (注意:這是一個通用的軟件概念,並非特定於Python;請參閱其他人的帖子以獲取有關在Python中實現靜態的信息)。


絕對是的,Python本身並沒有明確的靜態數據成員,但我們可以這樣做

class A:
    counter =0
    def callme (self):
        A.counter +=1
    def getcount (self):
        return self.counter  
>>> x=A()
>>> y=A()
>>> print(x.getcount())
>>> print(y.getcount())
>>> x.callme() 
>>> print(x.getcount())
>>> print(y.getcount())

產量

0
0
1
1

說明

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"

關於Python屬性查找的一個非常有趣的地方是它可以用來創建“ virtual變量”:

class A(object):

  label="Amazing"

  def __init__(self,d): 
      self.data=d

  def say(self): 
      print("%s %s!"%(self.label,self.data))

class B(A):
  label="Bold"  # overrides A.label

A(5).say()      # Amazing 5!
B(3).say()      # Bold 3!

通常在創建它們後沒有任何分配。 請注意,查找使用self因為儘管label在不與特定實例關聯的意義label是靜態的,但該值仍取決於實例的(類)。


關於靜態屬性和實例屬性需要注意的一件事情,如下例所示:

class my_cls:
  my_prop = 0

#static property
print my_cls.my_prop  #--> 0

#assign value to static property
my_cls.my_prop = 1 
print my_cls.my_prop  #--> 1

#access static property thru' instance
my_inst = my_cls()
print my_inst.my_prop #--> 1

#instance property is different from static property 
#after being assigned a value
my_inst.my_prop = 2
print my_cls.my_prop  #--> 1
print my_inst.my_prop #--> 2

這意味著在將值賦給實例屬性之前,如果我們試圖通過'實例訪問屬性,則使用靜態值。 在python類中聲明的每個屬性在內存中總是有一個靜態槽





class-variables