Pytanie Czy są możliwe statyczne zmienne klasy?


Czy w pythonie można mieć statyczne zmienne lub metody klasy? Jaka składnia jest wymagana, aby to zrobić?


1505
2017-09-16 01:46


pochodzenie




Odpowiedzi:


Zmienne zadeklarowane w definicji klasy, ale nie wewnątrz metody, to zmienne klasyczne lub statyczne:

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

Tak jak @millerdev wskazuje, że tworzy to na poziomie klasy i zmienna, ale różni się od każdego poziomu instancji i zmienna, więc mógłbyś

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

Różni się od C ++ i Java, ale nie różni się tak bardzo od C #, gdzie nie można uzyskać dostępu do statycznego elementu za pomocą odwołania do instancji.

Widzieć co ma do powiedzenia tutorial Pythona na temat klas i obiektów klasowych.

@Steve Johnson już odpowiedział na temat metody statyczne, również udokumentowane w "Wbudowane funkcje" w bibliotece Python Reference.

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

@beidy poleca classmethods nad metodzie statycznej, jako że metoda następnie otrzymuje typ klasy jako pierwszy argument, ale wciąż jestem trochę nieostrożny w kwestii zalet tego podejścia w stosunku do metody statycznej. Jeśli ty też jesteś, to prawdopodobnie to nie ma znaczenia.


1479
2017-09-16 01:51



Uczę się Pythona, ale zalety @classmethod koniec @staticmethod AFAIK polega na tym, że zawsze otrzymujesz nazwę klasy, na którą została wywołana metoda, nawet jeśli jest to podklasa. W statycznej metodzie brakuje tych informacji, więc nie można na przykład wywołać metody nadpisanej. - Seb
@JollySin w pythonic sposób dla stałych jest nie rosną klasy dla stałych. Po prostu trochę const.py z PI = 3.14 i możesz go zaimportować wszędzie. from const import PI - Giszmo
Ta odpowiedź może zmylić problem ze statyczną zmienną. Najpierw, i = 3 jest nie zmienną statyczną, jest atrybutem klasy, a ponieważ różni się od atrybutu na poziomie instancji i to robi nie zachowywać się jak zmienna statyczna w innych językach. Widzieć Odpowiedź millerdev, Odpowiedź Yanna, i moja odpowiedź poniżej. - Rick Teachey
więc tylko jedna kopia i(zmienna statyczna) będzie w pamięci, nawet jeśli utworzę setki instancji tej klasy? - sdream
Dla każdego, kto interesuje się kim Daniel jest wymieniony w komentarzu @Dubslow, jest millerdev (droga powrotna) - HeyJude


@Blair Conrad powiedział, że zmienne statyczne zadeklarowane w definicji klasy, ale nie wewnątrz metody to zmienne klasowe lub "statyczne":

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

Tu jest kilka gotcha. Kontynuacja z powyższego przykładu:

>>> 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__
{}

Zwróć uwagę na zmienną instancji t.i został zsynchronizowany ze zmienną "statyczną" klasy, gdy atrybut i został ustawiony bezpośrednio t. To dlatego, że i został ponownie związany w t przestrzeń nazw, która różni się od Test przestrzeń nazw. Jeśli chcesz zmienić wartość zmiennej "statycznej", musisz ją zmienić w zakresie (lub obiekcie), w którym została pierwotnie zdefiniowana. Umieszczam "static" w cudzysłowach, ponieważ Python tak naprawdę nie ma zmiennych statycznych w takim sensie, jak C ++ i Java.

Chociaż nie mówi nic konkretnego o statycznych zmiennych lub metodach, to Samouczek w języku Python zawiera istotne informacje klasy i obiekty klasy.

@Steve Johnson również odpowiedział na temat metod statycznych, również udokumentowanych w "Wbudowanych funkcjach" w Python Library Reference.

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

@beid wspomniał również o metodzie class, która jest podobna do metody static. Pierwszym argumentem classmethod jest obiekt klasy. Przykład:

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

Pictorial Representation Of Above Example


525
2017-09-16 03:04



Proponuję nieco rozszerzyć ten przykład: jeśli po ustawieniu Test.i = 6, wtedy utworzysz nowy obiekt (np. U = Test ()), nowy obiekt "odziedziczy" nową wartość klasy (np. ui == 6) - Mark
Sposób na zachowanie synchronizacji zmiennych statycznych polega na ich właściwościach: class Test(object):, _i = 3, @property, def i(self),return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Teraz możesz zrobić x = Test(), x.i = 12, assert x.i == Test.i. - Rick Teachey
Mogę powiedzieć, że wszystkie zmienne są początkowo statyczne, a następnie uzyskanie dostępu do instancji sprawia, że ​​zmienne instancji w środowisku wykonawczym? - Ali
Być może jest to interesujące: jeśli zdefiniujesz metodę w Testie, która zmienia Test.i, to wpłynie to na OBU Test.i i wartości t.i. - Pablo


Metody statyczne i klasy

Jak zauważyły ​​inne odpowiedzi, metody statyczne i klasy można łatwo zrealizować za pomocą wbudowanych dekoratorów:

class Test(object):

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

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

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

Jak zwykle pierwszy argument do MyMethod() jest powiązany z obiektem klasy class. W przeciwieństwie do pierwszego argumentu MyClassMethod() jest związany z samym obiektem klasy (np. w tym przypadku, Test). Dla MyStaticMethod()żaden z argumentów nie jest związany, a posiadanie argumentów w ogóle jest opcjonalne.

"Zmienne statyczne"

Jednak implementacja "zmiennych statycznych" (cóż, zmienny zmienne statyczne, w każdym razie, jeśli to nie jest sprzeczność w kategoriach ...) nie jest tak proste. Jako millerdev wskazał w swojej odpowiedzi, problem polega na tym, że atrybuty klasowe Pythona nie są naprawdę "statycznymi zmiennymi". Rozważać:

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

To dlatego, że linia x.i = 12 dodał nowy atrybut instancji i do x zamiast zmieniać wartość Test klasa i atrybut.

Częściowy oczekiwane zachowanie zmiennych statycznych, tj. synchronizacja atrybutu między wieloma instancjami (ale nie z samą klasą; patrz "gotcha" poniżej), można to osiągnąć przez przekształcenie atrybutu class w właściwość:

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)

Teraz możesz zrobić:

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

Zmienna statyczna pozostanie teraz zsynchronizowana między wszystkimi instancjami klasy.

(UWAGA: To znaczy, chyba że instancja klasy zdecyduje się zdefiniować własną wersję _i! Ale jeśli ktoś zdecyduje się to zrobić, zasługują na to, co dostają, prawda?

Zauważ, że technicznie rzecz biorąc, i nadal nie jest w ogóle "zmienną statyczną"; to jest property, który jest specjalnym rodzajem deskryptora. Jednakże property zachowanie jest teraz równoznaczne z (zmienną) statyczną zmienną zsynchronizowaną ze wszystkimi instancjami klasy.

Niezmienne "Zmienne statyczne"

Aby uzyskać niezmienne zachowanie zmiennych statycznych, po prostu pomiń property seter:

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)

Teraz próbuję ustawić instancję i atrybut zwróci wartość AttributeError:

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

Jeden Gotcha, który będzie świadomy

Zauważ, że powyższe metody działają tylko z instancje z twojej klasy - będą nie praca podczas korzystania z samej klasy. Na przykład:

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'

Linia assert Test.i == x.i powoduje błąd, ponieważ i atrybut Test i x są dwoma różnymi obiektami.

Wiele osób uzna to za zaskakujące. Jednak nie powinno tak być. Jeśli wrócimy i sprawdzimy naszą Test definicja klasy (druga wersja), odnotowujemy tę linię:

    i = property(get_i) 

Najwyraźniej członek i z Test musi być property obiekt, który jest typem obiektu zwróconego z property funkcjonować.

Jeśli uznasz powyższe za mylące, najprawdopodobniej wciąż myślisz o tym z perspektywy innych języków (np. Java lub c ++). Powinieneś pójść na studia property obiekt, o kolejności, w której zwracane są atrybuty Pythona, protokół deskryptora i kolejność rozstrzygania metod (MRO).

Poniżej przedstawiam rozwiązanie powyższego "gotcha"; jednak sugerowałbym - usilnie - że nie próbuj robić czegoś takiego, dopóki - przynajmniej nie zrozumiesz dokładnie, dlaczego assert Test.i = x.i powoduje błąd.

PRAWDZIWE, RZECZYWISTE Zmienne statyczne - Test.i == x.i

Poniżej przedstawiam rozwiązanie (Python 3) wyłącznie w celach informacyjnych. Nie aprobuję tego jako "dobrego rozwiązania". Mam wątpliwości, czy emulacja zmiennego zachowania statycznego innych języków w Pythonie jest rzeczywiście konieczna. Jednak niezależnie od tego, czy jest on rzeczywiście użyteczny, poniższe informacje powinny pomóc w lepszym zrozumieniu działania Pythona.

UPDATE: ta próba jest naprawdę okropny; jeśli nalegasz na zrobienie czegoś takiego (podpowiedź: proszę nie, Python jest bardzo eleganckim językiem i but-horning go do zachowywania się jak inny język nie jest po prostu konieczny), użyj kodu w Odpowiedź Ethana Furmana zamiast.

Emulacja zachowania zmiennych statycznych innych języków za pomocą metaklasy

Metaclass jest klasą klasy. Domyślna metaklasa dla wszystkich klas w Pythonie (tj. Klasy "nowego stylu" po Pythonie 2.3) type. Na przykład:

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

Możesz jednak zdefiniować własną metaclass w następujący sposób:

class MyMeta(type): pass

I zastosuj go do swojej własnej klasy w ten sposób (tylko Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Poniżej znajduje się stworzony przeze mnie metaclass, który próbuje emulować zachowanie "statycznej zmiennej" w innych językach. Działa to zasadniczo poprzez zastąpienie domyślnego programu pobierającego, ustawiającego i usuwającego z wersjami, które sprawdzają, czy żądany atrybut jest "statyczną zmienną".

Katalog "zmiennych statycznych" przechowywany jest w StaticVarMeta.statics atrybut. Wszystkie żądania atrybutów są początkowo próbowane do rozwiązania przy użyciu zastępczej kolejności rozwiązywania. Nazwałem to "statyczną rozdzielczością rozdzielczości" lub "SRO". Odbywa się to poprzez wyszukiwanie żądanego atrybutu w zbiorze "zmiennych statycznych" dla danej klasy (lub jej klas macierzystych). Jeśli atrybut nie pojawia się w "SRO", klasa powróci do domyślnego zachowania get / set / delete (tj. "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

141
2017-12-19 15:16



Próbowałem użyć twojej drogi, ale stanąłem przed problemem, proszę spojrzeć na moje pytanie tutaj stackoverflow.com/questions/29329850/get-static-variable-value - Muhammed Refaat
@RickTeachey: Domyślam się, że powinieneś ogólnie zobaczyć wszystko, co robisz na Instancji klasy Test (przed użyciem go do tworzenia instancji) jako domeny meta-programowania? Na przykład, zmieniając zachowanie klasowe, robisz Test.i = 0 (tutaj po prostu całkowicie zniszcz obiekt własności). Domyślam się, że mechanizm "właściwości" uruchamia się tylko w przypadku dostępu do właściwości instancji klasy (chyba, że ​​zmienisz podstawowe zachowanie, używając prawdopodobnie meta-klasy jako pośrednika). Przy okazji, proszę ukończyć tę odpowiedź :-) - Ole Thomsen Buus
@RickTeachey Dzięki :-) Twój metaclass na końcu jest interesujący, ale w rzeczywistości jest nieco zbyt skomplikowany jak na mój gust. Może to być przydatne w dużych ramach / aplikacjach, gdzie ten mechanizm jest absolutnie wymagany. W każdym razie jest to przykładem tego, że jeśli naprawdę potrzebne jest nowe (złożone), inne niż domyślne metapoziomy, Python to umożliwia :) - Ole Thomsen Buus
@OleThomsenBuus: Sprawdź moja odpowiedź dla prostszego metaclass, który wykonuje zadanie. - Ethan Furman
@taper Masz rację; Zmieniłem odpowiedź, aby rozwiązać problem (nie mogę uwierzyć, że tak długo siedział źle!). Przepraszam za zamieszanie. - Rick Teachey


Możesz również dodawać zmienne klas do zajęć w locie

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

Instancje klasy mogą zmieniać zmienne klasy

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

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

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

23
2017-09-17 08:06



Czy zmienne nowej klasy zostaną zachowane, nawet jeśli klasa zostanie zaimportowana do innego modułu? - zakdances
Tak. Klasy są efektywnymi singletonami, niezależnie od obszaru nazw, z którego je wywołujesz. - Pedro


Osobiście użyłbym metody class zawsze, gdy potrzebowałem metody statycznej. Głównie dlatego, że mam klasę jako argument.

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

lub użyj dekoratora

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

Dla właściwości statycznych .. Czas, aby wyszukać jakąś definicję Pythona. Zmienna może zawsze ulec zmianie. Są dwa typy z nich zmienne i niezmienne. Są też atrybuty klas i atrybuty instancji. Nic tak naprawdę nie przypomina statycznych atrybutów w znaczeniu java i c ++

Po co używać metody statycznej w znaczeniu pythonic, jeśli nie ma ona żadnego związku z klasą! Gdybym był tobą, chciałbym użyć metody classmethod lub zdefiniować metodę niezależną od klasy.


12
2017-09-16 02:02



Zmienne nie są zmienne lub niezmienne; obiekty są. (Jednak obiekt może, z różnym powodzeniem, próbować zapobiec przypisaniu do niektórych jego atrybutów.) - Davis Herring


Metody statyczne w Pythonie są wywoływane classmethods. Spójrz na poniższy kod

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

Zauważ, że kiedy wywołujemy tę metodę myInstanceMethod, otrzymujemy błąd. Dzieje się tak dlatego, że wymaga ona wywołania tej metody dla instancji tej klasy. Metoda myStaticMethod jest ustawiony jako metoda klasy przy użyciu dekorator  @classmethod.

Tylko dla kopnięć i chichotów, moglibyśmy zadzwonić myInstanceMethod na klasie, przekazując instancję klasy, tak jak poniżej:

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

11
2017-09-16 02:05





Warto zwrócić uwagę na właściwości statyczne i właściwości instancji, pokazane w poniższym przykładzie:

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

Oznacza to, że przed przypisaniem wartości do właściwości instancji, jeśli spróbujemy uzyskać dostęp do właściwości przez instancję, używana jest wartość statyczna. Każda właściwość zadeklarowana w klasie Pythona ma zawsze stały slot w pamięci.


10
2018-03-08 06:06





Podczas definiowania jakiejś zmiennej składowej poza jakąkolwiek metodą składową, zmienna może być statyczna lub niestatyczna w zależności od sposobu wyrażenia zmiennej.

  • CLASSNAME.var jest zmienną statyczną
  • INSTANCENAME.var nie jest zmienną statyczną.
  • własna klasa wewnętrzna self.var nie jest zmienną statyczną.
  • var wewnątrz funkcji członka klasy nie jest zdefiniowany.

Na przykład:

#!/usr/bin/python

class A:
    var=1

    def printvar(self):
        print "self.var is %d" % self.var
        print "A.var is %d" % A.var


    a = A()
    a.var = 2
    a.printvar()

    A.var = 3
    a.printvar()

Rezultaty są

self.var is 2
A.var is 1
self.var is 2
A.var is 3

7
2018-03-26 17:56





Można również wymusić statyczną statystykę za pomocą metaklasy.

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

Następnie za każdym razem, gdy przypadkowo spróbujesz zainicjować Moja klasa otrzymasz StaticClassError.


6
2017-11-20 12:06



Dlaczego jest to nawet klasa, jeśli nie zamierzasz go utworzyć? To wydaje się być skręceniem Pythona, aby przekształcić go w Javę .... - Ned Batchelder
The Idiom Borg jest lepszym sposobem na poradzenie sobie z tym. - Rick Teachey


Możliwe jest static zmienne klasowe, ale prawdopodobnie nie warte wysiłku.

Oto proof-of-concept napisany w Pythonie 3 - jeśli którykolwiek z dokładnych szczegółów jest błędny, kod można zmodyfikować tak, aby pasował dokładnie do tego, co rozumiesz przez 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)

i w użyciu:

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')

i niektóre testy:

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

4
2018-03-25 09:02



Jest to o wiele prostsze niż moja próba powyżej. Brawo! - Rick Teachey