Czy w Pythonie są możliwe zmienne klasy statycznej?

1948

Czy w Pythonie można mieć zmienne lub metody klasy statycznej? Jaka składnia jest do tego wymagana?

Andrew Walker
źródło

Odpowiedzi:

1900

Zmienne zadeklarowane w definicji klasy, ale nie wewnątrz metody, są zmiennymi klasy lub statycznymi:

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

Jak wskazuje @ millerdev , tworzy to izmienną na poziomie klasy , ale różni się ona od dowolnej izmiennej na poziomie instancji , więc możesz mieć

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

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

Zobacz, co ma do powiedzenia samouczek języka Python na temat klas i obiektów klas .

@Steve Johnson już odpowiedział na pytania dotyczące metod statycznych , udokumentowane również w części „Funkcje wbudowane” w dokumentacji Python Library Reference .

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

@beidy zaleca classmethod s zamiast staticmethod, ponieważ metoda następnie odbiera typ klasy jako pierwszy argument, ale nadal jestem trochę rozmyślany nad zaletami tego podejścia w stosunku do staticmethod. Jeśli ty też, to prawdopodobnie nie ma znaczenia.

Blair Conrad
źródło
11
Właśnie uczę się języka Python, ale zaletą @classmethodnad @staticmethodAFAIK jest to, że zawsze dostajesz nazwę klasy, na której wywołano metodę, nawet jeśli jest to podklasa. Metoda statyczna nie ma tej informacji, więc nie może na przykład wywołać metody przesłoniętej.
Seb
49
@ theJollyS W pythonowym sposobie dla stałych nie jest rozwijana klasa dla stałych. Po prostu niektóre const.pyz PI = 3.14i można go importować wszędzie. from const import PI
Giszmo
30
Ta odpowiedź może wprowadzić w błąd zagadnienie zmiennej statycznej. Przede wszystkim, i = 3jest nie zmienna statyczna, jest atrybutem klasy, a ponieważ różni się od atrybutu instancji poziomie ito robi nie zachowywać się jak zmiennej statycznej w innych językach. Zobacz odpowiedź millerdev jest , odpowiedź Yann jest i moją odpowiedź poniżej.
Rick wspiera Monikę
2
więc tylko jedna kopia i(zmiennej statycznej) będzie w pamięci, nawet jeśli utworzę setki instancji tej klasy?
sdream
2
Dla wszystkich zainteresowanych, o których Daniel wspomniał w komentarzu @Dubslow, jest to millerdev ( maszyna powrotna )
HeyJude
618

@Blair Conrad powiedział, że zmienne statyczne zadeklarowane w definicji klasy, ale nie w metodzie są zmiennymi klasy lub „statycznymi”:

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

Jest tu kilka gotcha. Kontynuując powyższy przykład:

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

Zauważ, jak zmienna instancji t.inie zsynchronizowała się ze zmienną klasy „statycznej”, gdy atrybut izostał ustawiony bezpośrednio na t. Jest tak, ponieważ izostał ponownie związany w tprzestrzeni nazw, która różni się od Testprzestrzeni nazw. Jeśli chcesz zmienić wartość zmiennej „statycznej”, musisz ją zmienić w zakresie (lub obiekcie), w którym została pierwotnie zdefiniowana. W cudzysłowie wstawiam „static”, ponieważ Python tak naprawdę nie ma zmiennych statycznych w takim sensie, jak C ++ i Java.

Chociaż nie mówi nic konkretnego o zmiennych statycznych lub metodach, samouczek języka Python zawiera pewne istotne informacje na temat klas i obiektów klas .

@ Steve Johnson również odpowiedziała na temat metod statycznych, udokumentowanych również w części „Funkcje wbudowane” w dokumentacji Python Library Reference.

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

@beid wspomniał także o classmethod, która jest podobna do staticmethod. Pierwszym argumentem metody metody 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 be the same as Test.i = arg1

Obrazowe przedstawienie powyższego przykładu

millerdev
źródło
3
Proponuję nieco rozszerzyć przykład: jeśli po ustawieniu Test.i = 6 utworzysz instancję nowego obiektu (np. U = Test ()), nowy obiekt „odziedziczy” nową wartość klasy (np. ui == 6)
Mark
2
Sposobem na utrzymanie zmienne statyczne zsynchronizowane jest, aby im właściwości: 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żna zrobić x = Test(), x.i = 12, assert x.i == Test.i.
Rick wspiera Monikę
1
Więc mogę powiedzieć, że wszystkie zmienne są początkowo statyczne, a następnie dostęp do instancji sprawia, że ​​zmienne instancji w czasie wykonywania?
Ali
Być może jest to interesujące: jeśli zdefiniujesz metodę w teście, która zmienia Test.i, wpłynie to na OBIE wartości Test.i i ti.
Pablo,
@millerdev, tak jak wspominałeś, że Python nie ma zmiennych statycznych, tak jak C ++ lub JAVA. Więc czy można powiedzieć, że Test.i jest bardziej zmienną klasową niż statyczną?
Tyto
197

Metody statyczne i klasowe

Jak zauważyły ​​inne odpowiedzi, metody statyczne i klasowe można łatwo osiągnąć 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 instancji klasy. Natomiast pierwszy argument związanyMyClassMethod() jest z samym obiektem klasy (np. W tym przypadku Test). Ponieważ 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óż, zmiennych zmiennych statycznych, zresztą, jeśli nie jest to sprzeczność pod względem ...) nie jest tak prosta. Jak zauważył millerdev w swojej odpowiedzi , problem polega na tym, że atrybuty klas Pythona nie są tak naprawdę „zmiennymi statycznymi”. 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 został dodany nowy atrybut instancji iaby xzamiast zmieniać wartość Testklasy iatrybutu.

Częściowy oczekiwane zachowanie zmiennej statycznej, tj. Synchronizacja atrybutu między wieloma instancjami (ale nie z samą klasą; patrz „gotcha” poniżej), można osiągnąć, zmieniając atrybut klasy we 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 będzie teraz synchronizowana między wszystkimi instancjami klas .

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

Należy zauważyć, że technicznie rzecz biorąc, inadal nie jest wcale „zmienną statyczną”; to jestproperty , co jest szczególnym rodzajem deskryptora. Jednak propertyzachowanie jest teraz równoważne (zmiennej) zmiennej statycznej zsynchronizowanej we wszystkich instancjach klasy.

Niezmienne „zmienne statyczne”

Aby zachować niezmienne zachowanie zmiennej statycznej, po prostu pomiń propertysetter:

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óba ustawienia iatrybutu instancji zwróci AttributeError:

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

Trzeba być świadomym

Należy zauważyć, że powyższe metody pracy jedynie z wystąpień w klasie - będą nie pracy przy użyciu 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.igeneruje błąd, ponieważ iatrybut Testi xsą dwoma różnymi obiektami.

Wiele osób uzna to za zaskakujące. Jednak nie powinno tak być. Jeśli wrócimy i sprawdzimy Testdefinicję naszej klasy (druga wersja), zauważymy następujący wiersz:

    i = property(get_i) 

Oczywiste jest, że element iz Testmusi być propertyobiekt, który jest typ obiektu zwracane z propertyfunkcji.

Jeśli powyższe stwierdzenie jest mylące, najprawdopodobniej nadal myślisz o tym z perspektywy innych języków (np. Java lub c ++). Powinieneś studiowaćproperty obiekt, na temat kolejności zwracania atrybutów Python, protokołu deskryptora i kolejności rozwiązywania metod (MRO).

Poniżej przedstawiam rozwiązanie powyższej „gotcha”; sugerowałbym jednak - stanowczo - że nie próbujesz robić czegoś takiego, dopóki - przynajmniej - nie do końca zrozumiesz, dlaczegoassert Test.i = x.i powoduje błąd.

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

Przedstawiam poniższe rozwiązanie (Python 3) wyłącznie w celach informacyjnych. Nie popieram tego jako „dobrego rozwiązania”. Mam wątpliwości, czy emulowanie zachowań zmiennych statycznych innych języków w Pythonie jest rzeczywiście konieczne. Jednak niezależnie od tego, czy jest to rzeczywiście przydatne, poniższe informacje powinny pomóc lepiej zrozumieć, jak działa Python.

AKTUALIZACJA: ta próba jest naprawdę okropna ; jeśli nalegasz na zrobienie czegoś takiego (podpowiedź: nie rób; Python jest bardzo eleganckim językiem i zmuszanie go do zachowywania się tak, jakby inny język nie był po prostu konieczny), zamiast tego użyj kodu w odpowiedzi Ethana Furmana .

Emulacja zachowania zmiennych statycznych innych języków przy użyciu metaklasy

Metaklasa jest klasą klasy. Domyślna metaklasa dla wszystkich klas w Pythonie (to znaczy 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ą metaklasę w następujący sposób:

class MyMeta(type): pass

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

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Poniżej utworzyłem metaklasę, która próbuje naśladować zachowanie „zmiennych statycznych” innych języków. Zasadniczo działa, zastępując domyślny moduł pobierający, ustawiający i usuwający wersjami, które sprawdzają, czy żądany atrybut jest „zmienną statyczną”.

Katalog „zmiennych statycznych” jest przechowywany w StaticVarMeta.staticsatrybucie. Wszystkie żądania atrybutów są początkowo próbowane do rozwiązania przy użyciu zastępczej kolejności rozstrzygania. Nazwałem to „statycznym porządkiem rozdzielczości” lub „SRO”. Odbywa się to poprzez wyszukiwanie żądanego atrybutu w zbiorze „zmiennych statycznych” dla danej klasy (lub jej klas nadrzędnych). Jeśli atrybut nie pojawi się w „SRO”, klasa powróci do domyślnego zachowania get / set / delete atrybutu (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
Rick wspiera Monikę
źródło
Próbowałem użyć twojej drogi, ale napotkałem problem, uprzejmie spójrz na moje pytanie tutaj stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat
@RickTeachey: Myślę, że ogólnie powinieneś zobaczyć wszystko, co robisz w Instancji klasy Test(przed użyciem jej do tworzenia instancji), jako należące do dziedziny metaprogramowania? Na przykład zmieniasz zachowanie klasy, robiąc Test.i = 0(tutaj po prostu całkowicie niszczysz obiekt własności). Wydaje mi się, że „mechanizm własności” włącza się tylko w przypadku dostępu do właściwości w instancjach klasy (chyba że zmienisz podstawowe zachowanie za pomocą meta-klasy jako pośredniej). Przy okazji, proszę zakończyć tę odpowiedź :-)
Ole Thomsen Buus
1
@RickTeachey Dzięki :-) Twoja metaklasa jest w końcu interesująca, ale w rzeczywistości jest zbyt skomplikowana dla moich upodobań. Może być przydatny w dużych ramach / aplikacjach, w których ten mechanizm jest absolutnie wymagany. W każdym razie stanowi to przykład, że jeśli naprawdę potrzebne są nowe (złożone) niestandardowe meta-zachowanie, Python umożliwia: :)
Ole Thomsen Buus
1
@OleThomsenBuus: Sprawdź moją odpowiedź na prostszą metaklasę, która spełnia swoje zadanie.
Ethan Furman
1
@ taper Masz rację; Zredagowałem odpowiedź, aby rozwiązać problem (nie mogę uwierzyć, że tak źle siedział tam tak długo!). Przepraszam za zamieszanie.
Rick obsługuje Monikę
33

Możesz także dodawać zmienne klas do klas 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 klas mogą zmieniać zmienne klas

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

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

>python test.py
[1]
[1, 1]
Gregory
źródło
3
Czy nowe zmienne klasowe zostaną zachowane, nawet jeśli klasa zostanie zaimportowana do innego modułu?
zakdances
Tak. Klasy są faktycznie singletonami, niezależnie od przestrzeni nazw, z której je wywołujesz.
Pedro
@Gregory powiedziałeś „I instancje klas mogą zmieniać zmienne klas” W rzeczywistości ten przykład nazywa się dostępem, a nie modyfikacją. Modyfikacji dokonał sam obiekt za pomocą własnej funkcji append ().
Amr ALHOSSARY
19

Osobiście używałbym metody klasowej, ilekroć potrzebowałem metody statycznej. Głównie dlatego, że dostaję klasę jako argument.

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

lub użyj dekoratora

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

W przypadku właściwości statycznych. Czas poszukać definicji w języku Python. Zmienna zawsze może się zmienić. Istnieją dwa rodzaje mutowalnych i niezmiennych. Ponadto istnieją atrybuty klas i instancji. Nic tak naprawdę jak atrybuty statyczne w sensie java & c ++

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

emb
źródło
1
Zmienne nie są zmienne ani niezmienne; obiekty są. (Jednak obiekt z różnym powodzeniem może próbować zapobiec przypisaniu do niektórych swoich atrybutów.)
Davis Herring,
Java i C ++ używają statycznego (złe użycie słowa, imho) dokładnie tak, jak używasz atrybutu instancja kontra klasa. Atrybut / metoda klasy jest statyczna w Javie i C ++, bez różnicy, z wyjątkiem tego, że w Pythonie pierwszym parametrem wywołania metody class jest klasa.
Angel O'Sphere
16

Jedną szczególną rzeczą do zapamiętania na temat właściwości statycznych i właściwości instancji, pokazanych 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 wystąpienia, jeśli spróbujemy uzyskać dostęp do właściwości przez wystąpienie, zostanie użyta wartość statyczna. Każda właściwość zadeklarowana w klasie python zawsze ma stałe miejsce w pamięci .

jondinham
źródło
16

Metody statyczne w pythonie są nazywane metodami klasy . Spójrz na następujący 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 metodę myInstanceMethod , pojawia się błąd. Jest tak, ponieważ wymaga wywołania tej metody na instancji tej klasy. Metodę myStaticMethod ustawia się jako metodę klasy za pomocą dekoratora @classmethod .

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

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method
Willurd
źródło
2
Umm ... metody statyczne są wykonane @staticmethod; @classmethodjest (oczywiście) dla metod klasowych (które są przede wszystkim przeznaczone do stosowania jako alternatywne konstruktory, ale mogą służyć jako szczypta jako metody statyczne, które otrzymują referencję do klasy, przez którą zostały wywołane).
ShadowRanger
11

Gdy zdefiniujesz jakąś zmienną składową 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ą.
  • self.var wewnątrz klasy 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()

Wyniki są

self.var is 2
A.var is 1
self.var is 2
A.var is 3
użytkownik2209576
źródło
Wgłębienie jest zepsute. To się nie powiedzie
Thomas Weller,
9

Możliwe są staticzmienne 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 nieprawidłowy, kod można dostosować tak, aby pasował do dowolnego znaczenia 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
Ethan Furman
źródło
8

Można również wymusić statyczność klasy 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

Za każdym razem, gdy przypadkiem spróbujesz zainicjować MyClass , otrzymasz błąd StaticClassError.

Bartosz Ptaszyński
źródło
4
Dlaczego jest to nawet klasa, jeśli nie zamierzasz jej tworzyć? To jest jak przekręcenie Pythona, aby zamienić go w Javę ....
Ned Batchelder
1
Borg idiom jest lepszy sposób, aby sobie z tym poradzić.
Rick wspiera Monikę
@NedBatchelder Jest to klasa abstrakcyjna, przeznaczona wyłącznie do podklas (i tworzenia instancji podklas)
stevepastelan 24.09.2018
1
Mam nadzieję, że podklasy nie używają super () do wywoływania __new__swoich rodziców ...
Ned Batchelder
7

Jednym z bardzo interesujących punktów dotyczących wyszukiwania atrybutów Pythona jest to, że można go używać do tworzenia „ zmiennych wirtualnych ”:

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!

Zwykle nie ma żadnych przypisań do nich po ich utworzeniu. Zauważ, że wyszukiwanie korzysta, selfponieważ chociaż labeljest statyczne w tym sensie, że nie jest powiązane z konkretną instancją, wartość nadal zależy od (klasy) instancji.

Davis Herring
źródło
6

W odniesieniu do tej odpowiedzi , dla stałej zmiennej statycznej można użyć deskryptora. Oto przykład:

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

w wyniku czego ...

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

Zawsze możesz zgłosić wyjątek, jeśli cicho ignorowanie wartości ustawienia ( passpowyżej) nie jest Twoją sprawą. Jeśli szukasz zmiennej klasy statycznej w języku 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

Spójrz na tę odpowiedź i oficjalne dokumenty HOWTO, aby uzyskać więcej informacji na temat deskryptorów.

Yann
źródło
2
Możesz także po prostu użyć @property, co jest tym samym, co użycie deskryptora, ale jest to o wiele mniej kodu.
Rick wspiera Monikę
6

Absolutnie tak, Python sam w sobie nie ma wyraźnie żadnego statycznego elementu danych, ale możemy to zrobić

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

wynik

0
0
1
1

wyjaśnienie

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"
Mari Selvan
źródło
6

Tak, zdecydowanie możliwe jest pisanie zmiennych statycznych i metod w Pythonie.

Zmienne statyczne: Zmienne zadeklarowane na poziomie klasy nazywane są zmiennymi statycznymi, do których można uzyskać bezpośredni dostęp za pomocą nazwy klasy.

    >>> class A:
        ...my_var = "shagun"

    >>> print(A.my_var)
        shagun

Zmienne instancji: Zmienne, które są powiązane i dostępne przez instancję klasy, są zmiennymi instancji.

   >>> a = A()
   >>> a.my_var = "pruthi"
   >>> print(A.my_var,a.my_var)
       shagun pruthi

Metody statyczne: Podobnie jak zmienne, do metod statycznych można uzyskać bezpośredni dostęp za pomocą nazwy klasy. Nie ma potrzeby tworzenia instancji.

Należy jednak pamiętać, że metoda statyczna nie może wywoływać metody niestatycznej w Pythonie.

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!
Shagun Pruthi
źródło
4

Aby uniknąć potencjalnych nieporozumień, chciałbym skontrastować zmienne statyczne i niezmienne obiekty.

Niektóre prymitywne typy obiektów, takie jak liczby całkowite, zmiennoprzecinkowe, łańcuchy i zmienne są niezmienne w Pythonie. Oznacza to, że obiekt, do którego odnosi się dana nazwa, nie może się zmienić, jeśli należy do jednego z wyżej wymienionych typów obiektów. Nazwę można przypisać do innego obiektu, ale sam obiekt nie może zostać zmieniony.

Uczynienie zmiennej statyczną posuwa się o krok dalej, uniemożliwiając nazwę zmiennej, aby wskazywała na dowolny obiekt oprócz tego, na który obecnie wskazuje. (Uwaga: jest to ogólna koncepcja oprogramowania i nie jest specyficzna dla Pythona; zobacz posty innych osób, aby uzyskać informacje na temat implementacji statyki w Pythonie).

Ross
źródło
4

Najlepszym sposobem, jaki znalazłem, jest użycie innej klasy. Możesz utworzyć obiekt, a następnie użyć go na innych obiektach.

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

Na powyższym przykładzie stworzyłem klasę o nazwie staticFlag .

Ta klasa powinna prezentować statyczny var __success (Private Static Var).

tryIt klasa reprezentowała zwykłą klasę, której musimy użyć.

Teraz zrobiłem obiekt dla jednej flagi (staticFlag ). Ta flaga zostanie wysłana jako odniesienie do wszystkich zwykłych obiektów.

Wszystkie te obiekty są dodawane do listy tryArr.


Wyniki tego skryptu:

False
False
False
False
False
True
True
True
True
True
Tomer Zait
źródło
2

Zmienne statyczne w fabryce klas python3.6

Dla każdego, kto korzysta z fabryki klas z python3.6 i nowszymi , użyj nonlocalsłowa kluczowego, aby dodać go do zakresu / kontekstu tworzonej klasy w następujący sposób:

>>> 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
jmunsch
źródło
tak, ale w tym przypadku hasattr(SomeClass, 'x')jest False. Wątpię, czy to w ogóle ktoś rozumie przez zmienną statyczną.
Rick wspiera Monikę
@RickTeachey lol, widziałem twój statyczny zmienny kod, stackoverflow.com/a/27568860/2026508 +1, proszę pana, i pomyślałem, że hasattr tak nie działa? więc jest some_varniezmienny i statycznie zdefiniowany, czy nie? Co zewnętrzny dostęp gettera ma wspólnego z tym, że zmienna jest statyczna, czy nie? mam teraz tyle pytań. bardzo chciałbym usłyszeć odpowiedzi, kiedy będziesz miał czas.
jmunsch
Tak, ta metaklasa jest dość niedorzeczna. Nie jestem pewien, czy rozumiem pytania, ale moim zdaniem some_varpowyżej nie jest wcale członkiem klasy. W Pythonie dostęp do wszystkich członków klasy można uzyskać spoza klasy.
Rick wspiera Monikę
nonlocalKeywoard „wpada” zakres zmiennej. Zakres definicji treści klasy jest niezależny od zakresu, w którym się znajduje, kiedy mówisz nonlocal some_var, że tworzy po prostu nielokalne (czytaj: NIE w zakresie definicji definicji klasy) odwołanie do innego nazwanego obiektu. Dlatego nie jest dołączany do definicji klasy, ponieważ nie znajduje się w zakresie treści klasy.
Rick wspiera Monikę
1

Prawdopodobnie jest to włamanie, ale używałem eval(str)do uzyskania statycznego obiektu, swego rodzaju sprzeczności, w Pythonie 3.

Istnieje plik Records.py, który zawiera wyłącznie classobiekty zdefiniowane za pomocą metod statycznych i konstruktorów, które zapisują niektóre argumenty. Następnie z innego pliku .py import Recordsmuszę dynamicznie wybrać każdy obiekt, a następnie utworzyć go na żądanie zgodnie z typem wczytywanych danych.

Więc gdzie object_name = 'RecordOne' lub nazwę klasy, wzywam, cur_type = eval(object_name)a następnie w celu jej utworzenia. Robisz to. cur_inst = cur_type(args) Jednak zanim utworzysz instancję, możesz wywołać metody statyczne z, cur_type.getName()na przykład, czegoś w rodzaju abstrakcyjnej implementacji klasy bazowej lub innego celu. Jednak w backendie prawdopodobnie jest on tworzony w Pythonie i nie jest naprawdę statyczny, ponieważ eval zwraca obiekt .... który musiał zostać utworzony ... który daje statyczne zachowanie.

Christopher Hoffman
źródło
0

Za pomocą listy lub słownika można uzyskać „zachowanie statyczne” między instancjami.

class Fud:

     class_vars = {'origin_open':False}

     def __init__(self, origin = True):
         self.origin = origin
         self.opened = True
         if origin:
             self.class_vars['origin_open'] = True


     def make_another_fud(self):
         ''' Generating another Fud() from the origin instance '''

         return Fud(False)


     def close(self):
         self.opened = False
         if self.origin:
             self.class_vars['origin_open'] = False


fud1 = Fud()
fud2 = fud1.make_another_fud()

print (f"is this the original fud: {fud2.origin}")
print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is this the original fud: False
# is the original fud open: True

fud1.close()

print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is the original fud open: False
Sójka
źródło
0

Jeśli próbujesz udostępnić zmienną statyczną, na przykład zwiększając ją w innych instancjach, coś takiego jak ten skrypt działa dobrze:

# -*- coding: utf-8 -*-
class Worker:
    id = 1

    def __init__(self):
        self.name = ''
        self.document = ''
        self.id = Worker.id
        Worker.id += 1

    def __str__(self):
        return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')


class Workers:
    def __init__(self):
        self.list = []

    def add(self, name, doc):
        worker = Worker()
        worker.name = name
        worker.document = doc
        self.list.append(worker)


if __name__ == "__main__":
    workers = Workers()
    for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
        workers.add(item[0], item[1])
    for worker in workers.list:
        print(worker)
    print("next id: %i" % Worker.id)
Winter Squad
źródło