python class - 靜態類變量可能嗎?





static variable (14)


@Blair Conrad表示在類定義中聲明的靜態變量,但不在方法內的是類或“靜態”變量:

>>> class Test(object):
...     i = 3
...
>>> Test.i
3

這裡有幾個難題。 從上面的例子開始:

>>> t = Test()
>>> t.i     # static variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the static variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the static variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}

注意,當屬性i直接在t上設置時,實例變量ti與“static”類變量不同步。 這是因為it名稱空間內重新綁定,這與Test名稱空間不同。 如果要更改“靜態”變量的值,則必須在最初定義的範圍(或對象)內對其進行更改。 我將“static”放在引號中,因為在C ++和Java的意義上,Python並沒有真正的靜態變量。

儘管它沒有提及任何有關靜態變量或方法的具體內容,但Python教程還提供了有關類和類對象的一些相關信息。

@Steve Johnson也回答了有關靜態方法的問題,這些方法也記錄在Python庫參考中的“內置函數”下。

class Test(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

@beid也提到了classmethod,這與staticmethod類似。 classmethod的第一個參數是類對象。 例:

class Test(object):
    i = 3 # class (or static) variable
    @classmethod
    def g(cls, arg):
        # here we can use 'cls' instead of the class name (Test)
        if arg > cls.i:
            cls.i = arg # would the the same as  Test.i = arg1

是否有可能在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"



有可能有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中的靜態方法稱為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 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



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

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類中聲明的每個屬性在內存中總是有一個靜態槽




類工廠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中是不可變的。 這意味著如果它是前述對像類型之一,則由給定名稱引用的對像不能更改。 該名稱可以重新分配給不同的對象,但對象本身可能不會更改。

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




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

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

或使用裝飾器

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

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

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




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

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 staticFlag:
    def __init__(self):
        self.__success = False
    def isSuccess(self):
        return self.__success
    def succeed(self):
        self.__success = True

class tryIt:
    def __init__(self, staticFlag):
        self.isSuccess = staticFlag.isSuccess
        self.succeed = staticFlag.succeed

tryArr = []
flag = staticFlag()
for i in range(10):
    tryArr.append(tryIt(flag))
    if i == 5:
        tryArr[i].succeed()
    print tryArr[i].isSuccess()

通過上面的例子,我創建了一個名為staticFlag的類。

這個類應該呈現靜態var __success (私有靜態變量)。

tryIt類代表我們需要使用的常規課程。

現在我為一個標誌( staticFlag )創建了一個對象。 該標誌將作為所有常規對象的參考發送。

所有這些對像都被添加到tryArr列表中。

這個腳本結果:

False
False
False
False
False
True
True
True
True
True



關於這個answer ,對於一個常量靜態變量,你可以使用一個描述符。 這是一個例子:

class ConstantAttribute(object):
    '''You can initialize my value but not change it.'''
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        pass


class Demo(object):
    x = ConstantAttribute(10)


class SubDemo(Demo):
    x = 10


demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x

導致 ...

small demo 10
small subdemo 100
big demo 10
big subdemo 10

如果安靜地忽略設置值( pass上面的)不是你的事情,你總是可以引發異常。 如果您正在尋找C ++,Java風格的靜態類變量:

class StaticAttribute(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        self.value = val

看看這個答案和官方文檔HOWTO了解更多關於描述符的信息。




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

>>> 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]



讓我們假裝您想要生活在不可變的世界中,並且不想修改原始文件但是想要創建一個新的dict ,這是在原始文件中添加新密鑰的結果。

在Python 3.5+中,您可以:

params = {'a': 1, 'b': 2}
new_params = {**params, **{'c': 3}}

Python 2的等價物是:

params = {'a': 1, 'b': 2}
new_params = dict(params, **{'c': 3})

在以下任何一個之後:

params仍然等於{'a': 1, 'b': 2}

new_params等於{'a': 1, 'b': 2, 'c': 3}

有時你不想修改原件(你只想要添加到原件的結果)。 我覺得這是以下的一個令人耳目一新的替代品

params = {'a': 1, 'b': 2}
new_params = params.copy()
new_params['c'] = 3

要么

params = {'a': 1, 'b': 2}
new_params = params.copy()
new_params.update({'c': 3})

參考: https://.com/a/2255892/514866 : https://.com/a/2255892/514866







python class methods static class-variables