Różnica między metodą statyczną a metodą klasową


3579

Jaka jest różnica między funkcją ozdobione @staticmethodi jeden ozdobione @classmethod?


11
metody statyczne są czasem lepiej, ponieważ funkcje poziomu modułu w pythonie ze względu na czystość. Dzięki funkcji modułu łatwiej jest zaimportować tylko potrzebną funkcję i zapobiec niepotrzebnemu „.” składnia (patrzę na ciebie Objective-C). metody klasowe mają większe zastosowanie, ponieważ można je stosować w połączeniu z polimorfizmem do tworzenia funkcji „wzorca fabrycznego”. dzieje się tak, ponieważ metody klas odbierają klasę jako parametr niejawny.
FistOfFury,

27
tl; dr >> w porównaniu do normalnych metod, metody statyczne i metody klasowe można również uzyskać za pomocą klasy, ale w przeciwieństwie do metod klasowych, metody statyczne są niezmienne przez dziedziczenie.
imsrgadich,

4
Powiązana rozmowa Raymonda Hettingera na temat: youtube.com/watch?v=HTLu2DFOdTg
moooeeeep,

dokładniej youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 potrzebujesz metody klasy tylko dla alternatywnych konstruktorów. W przeciwnym razie możesz użyć staticmethod i uzyskać dostęp do dowolnego atrybutu klasy (poprzez. / Kropkę) przez nieco bardziej pouczające rzeczywiste „CLASSNAME” zamiast cls jak w classmethod
Robert Nowak

Odpowiedzi:


3136

Może trochę kodu przykładu pomoże: Zauważ różnicę w podpisach pożarowych foo, class_foooraz static_foo:

class A(object):
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)

    @classmethod
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" % x    

a = A()

Poniżej znajduje się zwykły sposób, w jaki instancja obiektu wywołuje metodę. Instancja obiektu a, jest domyślnie przekazywana jako pierwszy argument.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

W przypadku metod klasy klasa instancji obiektu jest domyślnie przekazywana jako pierwszy argument zamiast self.

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Możesz także zadzwonić class_fooza pomocą klasy. W rzeczywistości, jeśli zdefiniujesz coś jako metodę klasy, prawdopodobnie dzieje się tak dlatego, że zamierzasz wywoływać to z klasy, a nie z instancji klasy. A.foo(1)zgłosiłby błąd typu TypeError, ale A.class_foo(1)działa dobrze:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Jednym z zastosowań, które ludzie znaleźli dla metod klasowych, jest tworzenie dziedziczonych alternatywnych konstruktorów .


W przypadku metod statycznych ani self(instancja obiektu), ani cls(klasa) nie są domyślnie przekazywane jako pierwszy argument. Zachowują się jak zwykłe funkcje, tyle że można je wywoływać z instancji lub klasy:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Metody statyczne są używane do grupowania funkcji, które mają pewne logiczne połączenie z klasą do klasy.


foojest po prostu funkcją, ale kiedy wywołujesz a.foo, nie dostajesz po prostu funkcji, dostajesz „częściowo zastosowaną” wersję funkcji z instancją obiektu azwiązaną jako pierwszy argument funkcji. foooczekuje 2 argumentów, a a.footylko 1 argument.

ajest związany z foo. Oto, co należy rozumieć pod pojęciem „związany” poniżej:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Dzięki a.class_foo, anie jest związany class_foo, a klasa Ajest związana class_foo.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Tutaj przy pomocy metody statycznej, mimo że jest to metoda, a.static_foozwraca po prostu dobrą funkcję „ole” bez żadnych argumentów. static_foooczekuje 1 argumentu i a.static_foooczekuje również 1 argumentu.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

I oczywiście to samo dzieje się, gdy zamiast tego dzwonisz static_foodo klasy A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

182
Nie rozumiem, co jest haczykiem dla używania staticmethod. możemy po prostu użyć prostej funkcji poza klasą.
Alcott,

372
@Alcott: Możesz chcieć przenieść funkcję do klasy, ponieważ logicznie należy ona do klasy. W kodzie źródłowym Pythona (np. Multiprocessing, turtle, dist-packages) służy do „ukrywania” funkcji „private” z pojedynczym podkreśleniem w przestrzeni nazw modułu. Jego użycie jest jednak skoncentrowane w zaledwie kilku modułach - być może oznacza to, że jest to głównie stylistyka. Chociaż nie mogłem znaleźć żadnego takiego przykładu, @staticmethodmoże pomóc uporządkować kod, zastępując go podklasami. Bez niego warianty funkcji będą się unosić w przestrzeni nazw modułów.
unutbu

14
... wraz z wyjaśnieniem, gdzie i dlaczego należy użyć metody instancji, klasy lub metody statycznej. Nie podałeś ani jednego słowa na ten temat, ale OP nie zapytał o to.
MestreLion

106
@Alcott: jak powiedział unutbu, metody statyczne są cechą organizacyjną / stylistyczną. Czasami moduł ma wiele klas, a niektóre funkcje pomocnicze są logicznie powiązane z daną klasą, a nie z innymi, więc sensowne jest, aby nie „zanieczyszczać” modułu wieloma „wolnymi funkcjami”, i lepiej jest użyć statycznego metoda niż poleganie na złym stylu mieszania klas i definicji funkcji w kodzie, aby pokazać, że są „powiązane”
MestreLion

4
Jeszcze jedno zastosowanie @staticmethod- możesz go użyć, aby usunąć cruft. Wdrażam język programowania w Pythonie - funkcje zdefiniowane w bibliotece używają executemetody statycznej , w której funkcje zdefiniowane przez użytkownika wymagają argumentów instancji (tj. Treści funkcji). Ten dekorator eliminuje ostrzeżenia „nieużywanego parametru” w inspektorze PyCharm.
tehwalrus

798

Staticmethod jest metodą, która nie wie nic na temat klasy lub instancji był nazywany dalej. Dostaje tylko argumenty, które zostały przekazane, bez domyślnego pierwszego argumentu. Jest to w zasadzie bezużyteczne w Pythonie - możesz po prostu użyć funkcji modułu zamiast metody static.

Classmethod , z drugiej strony, jest to metoda, która zostaje przekazana klasę Nazwano na lub klasę instancji był nazywany dalej, jako pierwszy argument. Jest to przydatne, gdy chcesz, aby metoda była fabryką dla klasy: ponieważ pobiera rzeczywistą klasę, do której została wywołana, jako pierwszy argument, zawsze możesz utworzyć odpowiednią klasę, nawet jeśli w grę wchodzą podklasy. Obserwuj na przykład, w jaki dict.fromkeys()sposób metoda klasy zwraca instancję podklasy po wywołaniu w podklasie:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 

713
Metoda statyczna nie jest bezużyteczna - jest to sposób na umieszczenie funkcji w klasie (ponieważ logicznie tam należy), wskazując jednocześnie, że nie wymaga ona dostępu do klasy.
Tony Meyer,

135
Stąd tylko „zasadniczo” bezużyteczne. Taka organizacja, jak również wstrzykiwanie zależności, są poprawnymi zastosowaniami metod statycznych, ale ponieważ moduły, a nie klasy takie jak Java, są podstawowymi elementami organizacji kodu w Pythonie, ich użycie i użyteczność są rzadkie.
Thomas Wouters

40
Co jest logicznego w definiowaniu metody wewnątrz klasy, gdy nie ma ona nic wspólnego z klasą ani jej instancjami?
Ben James

106
Może ze względu na spadek? Metody statyczne mogą być dziedziczone i zastępowane tak samo, jak metody instancji i metody klas, a wyszukiwanie działa zgodnie z oczekiwaniami (w przeciwieństwie do Java). Metody statyczne nie są tak naprawdę rozstrzygane statycznie, niezależnie od tego, czy są wywoływane w klasie, czy instancji, więc jedyną różnicą między metodami klasowymi a statycznymi jest domyślny pierwszy argument.
haridsv

80
Tworzą również czystszą przestrzeń nazw i ułatwiają zrozumienie, że funkcja ma coś wspólnego z klasą.
Imbrondir,

149

Zasadniczo @classmethodtworzy metodę, której pierwszym argumentem jest klasa, z której została wywołana (a nie instancja klasy), @staticmethodnie ma żadnych domyślnych argumentów.


103

Oficjalne dokumenty Pythona:

@classmethod

Metoda klasy odbiera klasę jako domyślny pierwszy argument, podobnie jak metoda instancji odbiera instancję. Aby zadeklarować metodę klasową, użyj tego idiomu:

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

@classmethodForma jest funkcją dekorator - patrz opis definicji funkcji w definicji funkcji o szczegóły.

Można go wywołać w klasie (np. C.f()) Lub w instancji (np. C().f()). Instancja jest ignorowana z wyjątkiem swojej klasy. Jeśli dla klasy pochodnej wywoływana jest metoda klasy, obiekt klasy pochodnej jest przekazywany jako domyślny pierwszy argument.

Metody klasowe różnią się od metod statycznych C ++ lub Java. Jeśli chcesz, zobacz staticmethod()w tej sekcji.

@staticmethod

Metoda statyczna nie otrzymuje domyślnego pierwszego argumentu. Aby zadeklarować metodę statyczną, użyj tego idiomu:

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

@staticmethodForma jest funkcją dekorator - patrz opis definicji funkcji w definicji funkcji o szczegóły.

Można go wywołać w klasie (np. C.f()) Lub w instancji (np. C().f()). Instancja jest ignorowana z wyjątkiem swojej klasy.

Metody statyczne w Pythonie są podobne do metod znalezionych w Javie lub C ++. Bardziej zaawansowaną koncepcję można znaleźć classmethod()w tej sekcji.


Czy w dokumentach nie ma błędu? Nie powinno być w metodzie statycznej: „Zarówno instancja, jak i jej klasa są ignorowane”. zamiast „Instancja jest ignorowana z wyjątkiem swojej klasy.”?
mirek

Może to być błąd wycinania i wklejania, ale ściśle mówiąc, nie można wywołać metody w klasie, jeśli ją zignorujesz.
Aaron Bentley

76

Oto krótki artykuł na ten temat

Funkcja @staticmethod jest niczym więcej niż funkcją zdefiniowaną w klasie. Można to wywoływać bez wcześniejszego tworzenia klasy. Jego definicja jest niezmienna przez dziedziczenie.

Funkcja @classmethod można również wywoływać bez tworzenia instancji klasy, ale jej definicja jest zgodna z podklasą, a nie klasą nadrzędną, poprzez dziedziczenie. Jest tak, ponieważ pierwszym argumentem dla funkcji @classmethod zawsze musi być cls (klasa).


1
Czy to oznacza, że ​​używając metody statycznej zawsze jestem związany z klasą nadrzędną, a metodą klasową jestem związany z klasą, w której deklaruję metodę klasową (w tym przypadku podklasę)?
Mohan Gulati,

7
Nie. Używając metody statycznej nie jesteś wcale związany; nie ma niejawnego pierwszego parametru. Korzystając z metody klasy, otrzymujesz jako domyślny pierwszy parametr klasę, do której wywołałeś metodę (jeśli wywołałeś ją bezpośrednio w klasie) lub klasę instancji, do której wywołałeś metodę (jeśli wywołałeś ją w instancji).
Matt Anderson

6
Można go nieco rozwinąć, aby pokazać, że mając klasę jako pierwszy argument, metody klas mają bezpośredni dostęp do innych atrybutów i metod klasy, podczas gdy metody statyczne nie (muszą w tym celu zakodować
MyClass.attr

„Jego definicja jest niezmienna przez dziedziczenie”. nie ma żadnego sensu w Pythonie, możesz przesłonić metodę statyczną w porządku.
cz

68

Aby zdecydować, czy użyć @staticmethod czy @classmethod , musisz zajrzeć do swojej metody. Jeśli twoja metoda uzyskuje dostęp do innych zmiennych / metod w twojej klasie, użyj @classmethod . Z drugiej strony, jeśli twoja metoda nie dotyka żadnych innych części klasy, użyj @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

jaka byłaby korzyść z metody class i cls._counter vs. staticmethod i Apple._counter
Robert Nowak

1
cls._counternadal będzie, cls._counternawet jeśli kod zostanie umieszczony w innej klasie lub nazwa klasy zostanie zmieniona. Apple._counterjest specyficzny dla Appleklasy; dla innej klasy lub gdy nazwa klasy zostanie zmieniona, należy zmienić klasę odniesienia.
kiamlaluno

52

Jaka jest różnica między @staticmethod i @classmethod w Pythonie?

Być może widziałeś kod Pythona, taki jak ten pseudokod, który demonstruje sygnatury różnych typów metod i udostępnia dokumentację wyjaśniającą każdy z nich:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

Metoda wystąpienia normalnego

Najpierw wyjaśnię a_normal_instance_method. Jest to dokładnie nazywane „ metodą instancji ”. Gdy używana jest metoda instancji, jest ona używana jako funkcja częściowa (w przeciwieństwie do funkcji całkowitej, zdefiniowanej dla wszystkich wartości podczas przeglądania w kodzie źródłowym), która, gdy jest używana, pierwszy z argumentów jest predefiniowana jako instancja obiekt ze wszystkimi podanymi atrybutami. Jest z nim związana instancja obiektu i musi być wywołana z instancji obiektu. Zwykle uzyskuje dostęp do różnych atrybutów instancji.

Na przykład jest to instancja ciągu:

', '

jeśli użyjemy metody instancji joinna tym ciągu, aby dołączyć do innej iterowalnej, jest to oczywiście funkcja instancji, oprócz tego, że jest funkcją listy iterowalnej ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Metody powiązane

Metody instancji można powiązać za pomocą wyszukiwania przerywanego do późniejszego użycia.

Na przykład wiąże to str.joinmetodę z ':'instancją:

>>> join_with_colons = ':'.join 

Później możemy użyć tego jako funkcji, która ma już przypisany pierwszy argument. W ten sposób działa jak funkcja częściowa w instancji:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Metoda statyczna

Metoda statyczna nie przyjmuje instancji jako argumentu.

Jest bardzo podobny do funkcji na poziomie modułu.

Jednak funkcja poziomu modułu musi znajdować się w module i być specjalnie importowana do innych miejsc, w których jest używana.

Jednak jeśli jest on dołączony do obiektu, będzie wygodnie podążał za obiektem również poprzez importowanie i dziedziczenie.

Przykładem metody statycznej jest str.maketransprzeniesienie z stringmodułu w Pythonie 3. To sprawia, że ​​tabela translacji nadaje się do użycia przez str.translate. Wydaje się dość głupie, gdy jest używane z instancji ciągu, jak pokazano poniżej, ale importowanie funkcji z stringmodułu jest raczej niezdarne i fajnie jest móc wywoływać ją z klasy, jak wstr.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

W Pythonie 2 musisz zaimportować tę funkcję z coraz mniej użytecznego modułu łańcuchowego:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Metoda klasowa

Metoda klasy jest podobna do metody instancji, ponieważ przyjmuje domyślny pierwszy argument, ale zamiast przejmować instancję, bierze klasę. Często są one używane jako alternatywne konstruktory dla lepszego wykorzystania semantycznego i będą wspierać dziedziczenie.

Najbardziej kanonicznym przykładem wbudowanej metody klasy jest dict.fromkeys. Jest używany jako alternatywny konstruktor dict (dobrze nadaje się, gdy wiesz, jakie są twoje klucze i chcesz dla nich wartość domyślną).

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

Kiedy dyktujemy podklasę, możemy użyć tego samego konstruktora, który tworzy instancję podklasy.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Zobacz kod źródłowy pandy dla innych podobnych przykładów alternatywnych konstruktorów, a także oficjalną dokumentację Pythona na temat classmethodi staticmethod.


43

Zacząłem uczyć się języka programowania w C ++, potem w Javie, a potem w Pythonie, więc to pytanie również mnie bardzo niepokoiło, dopóki nie zrozumiałem prostego użycia każdego z nich.

Metoda klasy: Python w przeciwieństwie do Java i C ++ nie ma przeciążenia konstruktora. Aby to osiągnąć, możesz użyć classmethod. Poniższy przykład to wyjaśni

Rozważmy mamy Personklasę która przyjmuje dwa argumenty first_namea last_namei tworzy instancję Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Jeśli wymaganie pojawi się wtedy, gdy musisz utworzyć klasę używając tylko jednej nazwy, po prostu first_namenie możesz zrobić czegoś takiego w Pythonie.

Spowoduje to błąd podczas próby utworzenia obiektu (instancji).

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

Można jednak osiągnąć to samo, @classmethodjak wspomniano poniżej

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Metoda statyczna: jest to dość proste, nie jest powiązane z instancją ani klasą i można to po prostu wywołać za pomocą nazwy klasy.

Powiedzmy, że w powyższym przykładzie potrzebujesz walidacji, która first_namenie powinna przekraczać 20 znaków, możesz to po prostu zrobić.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

i możesz po prostu zadzwonić za pomocą class name

Person.validate_name("Gaurang Shah")

2
Jest to stary post, ale bardziej pythonowy sposób uzyskania konstruktora akceptującego jeden lub dwa argumenty def __init__(self, first_name, last_name="")zamiast metody klasowej get_person. Również wynik będzie dokładnie taki sam w tym przypadku.
akarilimano

31

Wydaje mi się, że lepszym pytaniem jest „Kiedy użyłbyś @classmethod vs. @staticmethod?”

@classmethod pozwala na łatwy dostęp do prywatnych członków powiązanych z definicją klasy. jest to świetny sposób na tworzenie singletonów lub istnieją klasy fabryczne, które kontrolują liczbę instancji tworzonych obiektów.

@staticmethod zapewnia marginalny wzrost wydajności, ale nie widziałem jeszcze produktywnego zastosowania metody statycznej w klasie, której nie można byłoby uzyskać jako samodzielnej funkcji poza klasą.


31

W Pythonie 2.4 dodano @dekoratory. Jeśli używasz Pythona <2.4, możesz użyć funkcji classmethod () i staticmethod ().

Na przykład, jeśli chcesz utworzyć metodę fabryczną (funkcja zwracająca instancję innej implementacji klasy w zależności od otrzymanego argumentu), możesz zrobić coś takiego:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

Zauważ również, że jest to dobry przykład użycia metody klasy i metody statycznej. Metoda statyczna wyraźnie należy do klasy, ponieważ korzysta ona wewnętrznie z Klastra klas. Metoda klasy potrzebuje tylko informacji o klasie, a nie instancji obiektu.

Kolejną zaletą przekształcenia _is_cluster_formetody w metodę klasy jest to, że podklasa może zdecydować o zmianie implementacji, być może dlatego, że jest dość ogólna i może obsługiwać więcej niż jeden typ klastra, więc samo sprawdzenie nazwy klasy nie wystarczy.


28

Metody statyczne:

  • Proste funkcje bez auto argumentów.
  • Pracuj nad atrybutami klas; nie w atrybutach instancji.
  • Może być wywoływany zarówno przez klasę, jak i instancję.
  • Do ich utworzenia służy wbudowana funkcja staticmethod ().

Korzyści z metod statycznych:

  • Lokalizuje nazwę funkcji w zakresie klasy
  • Przesuwa kod funkcji bliżej miejsca, w którym jest używany
  • Wygodniej jest importować niż funkcje na poziomie modułu, ponieważ każda metoda nie musi być specjalnie importowana

    @staticmethod
    def some_static_method(*args, **kwds):
        pass

Metody klasowe:

  • Funkcje, które mają pierwszy argument jako nazwa klasy.
  • Może być wywoływany zarówno przez klasę, jak i instancję.
  • Są one tworzone za pomocą wbudowanej funkcji classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass

22

@staticmethodpo prostu wyłącza domyślną funkcję jako deskryptor metody. classmethod zawija twoją funkcję w wywoływalny kontener, który jako pierwszy argument przekazuje odwołanie do klasy będącej właścicielem:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

W rzeczywistości classmethodma narzut związany ze środowiskiem uruchomieniowym, ale umożliwia dostęp do klasy będącej właścicielem. Alternatywnie polecam użycie metaklasy i umieszczenie klasowych metod na tej metaklasie:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

1
Jednym z możliwych wad metaklasy, które natychmiast mi się przydarza jest to, że nie można wywołać metody klasy bezpośrednio w instancji. c = C(); c.foo()podnosi AttributeError, musisz zrobić type(c).foo(). Może to być również uważane za cechę - nie mogę jednak wymyślić, dlaczego chcesz.
Aaron Hall

20

Ostateczny przewodnik na temat używania metod statycznych, klasowych lub abstrakcyjnych w Pythonie jest dobrym linkiem do tego tematu i podsumowano go następująco.

@staticmethodfunkcja jest niczym więcej niż funkcją zdefiniowaną w klasie. Można to wywoływać bez wcześniejszego tworzenia klasy. Jego definicja jest niezmienna przez dziedziczenie.

  • Python nie musi tworzyć instancji metody powiązania dla obiektu.
  • Ułatwia to czytelność kodu i nie zależy od samego stanu obiektu;

@classmethodfunkcja może być również wywoływana bez tworzenia instancji klasy, ale jej definicja jest następująca Podklasa, a nie klasa nadrzędna, poprzez dziedziczenie, może zostać zastąpiona przez podklasę. Jest tak, ponieważ pierwszym argumentem @classmethodfunkcji musi zawsze być cls (klasa).

  • Metody fabryczne , które są używane do tworzenia instancji dla klasy przy użyciu na przykład pewnego rodzaju przetwarzania wstępnego.
  • Metody statyczne wywołujące metody statyczne : jeśli podzielisz metody statyczne na kilka metod statycznych, nie powinieneś sztywno kodować nazwy klasy, ale używać metod klasowych

Dzięki @zangw - odziedziczona niezmienność funkcji statycznej wydaje się kluczową różnicą
hard_working_ant

18

Różni się tylko pierwszy argument :

  • metoda normalna: bieżący obiekt jest automatycznie przekazywany jako (dodatkowy) pierwszy argument
  • classmethod: klasa bieżącego obiektu jest automatycznie przekazywana jako (dodatkowy) argument pięści
  • staticmethod: żadne dodatkowe argumenty nie są automatycznie przekazywane. To, co przekazałeś do funkcji, dostajesz.

Bardziej szczegółowo...

normalna metoda

Gdy wywoływana jest metoda obiektu, automatycznie otrzymuje dodatkowy argument selfjako pierwszy argument. To znaczy metoda

def f(self, x, y)

należy wywołać z 2 argumentami. selfjest automatycznie przekazywany i jest to sam obiekt .

metoda klasowa

Kiedy metoda jest udekorowana

@classmethod
def f(cls, x, y)

automatycznie podawanym argumentem nie jest self , ale klasa self .

metoda statyczna

Kiedy metoda jest udekorowana

@staticmethod
def f(x, y)

metoda nie otrzymuje żadnego automatycznego argumentu. Podano tylko parametry, z którymi jest wywoływany.

zastosowania

  • classmethod jest najczęściej używany do alternatywnych konstruktorów.
  • staticmethodnie używa stanu obiektu. Może to być funkcja zewnętrzna w stosunku do klasy. Umieszcza tylko wewnątrz klasy dla grupowania funkcji o podobnej funkcjonalności (na przykład, jak Mathmetody statyczne klasy Java )
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

17

Pozwól mi najpierw powiedzieć podobieństwo między metodą ozdobioną @classmethod a @staticmethod.

Podobieństwo: oba można wywoływać w samej klasie , a nie tylko w instancji klasy. Oba są więc w pewnym sensie metodami klasy .

Różnica: Metoda klasy otrzyma samą klasę jako pierwszy argument, podczas gdy metoda statyczna nie.

Zatem metoda statyczna nie jest, w pewnym sensie, związana z samą klasą i po prostu wisi w niej tylko dlatego, że może mieć powiązaną funkcjonalność.

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

11

Innym aspektem dotyczącym metody statycznej vs metody klasowej jest dziedziczenie. Powiedz, że masz następującą klasę:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

A następnie chcesz zastąpić bar()w klasie podrzędnej:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

To działa, ale zauważ, że teraz bar()implementacja w klasie potomnej ( Foo2) nie może już korzystać z niczego specyficznego dla tej klasy. Na przykład powiedzmy Foo2miał metodę o nazwie magic(), której chcesz użyć w Foo2implementacji bar():

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

Rozwiązaniem tutaj byłoby zadzwonić Foo2.magic()w bar(), ale to jesteś powtarzając sobie (jeśli nazwaFoo2 zmian, trzeba pamiętać, aby zaktualizować tę bar()metodę).

Dla mnie jest to niewielkie naruszenie zasady otwartego / zamkniętego , ponieważ podjęta decyzja Fooma wpływ na twoją zdolność do refaktoryzacji wspólnego kodu w klasie pochodnej (tj. Jest mniej otwarta na rozszerzenie). Gdybyśmy bar()byli w classmethodporządku:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

Daje: In Foo2 MAGIC


7

Spróbuję wyjaśnić podstawową różnicę na przykładzie.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - możemy bezpośrednio wywoływać metody statyczne i klasowe bez inicjalizacji

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- Metoda statyczna nie może wywoływać metody self, ale może wywoływać inną metodę statyczną i klasową

3- Metoda statyczna należy do klasy i w ogóle nie będzie używać obiektu.

Metoda 4- klasy nie jest związana z obiektem, ale z klasą.


ad 2: Jesteś pewien? Jak metoda statyczna może wywołać metodę klasową? Nie ma odniesienia do niego (do swojej klasy).
mirek

7

@classmethod: może zostać użyty do stworzenia wspólnego globalnego dostępu do wszystkich instancji utworzonych dla tej klasy ..... jak np. aktualizacja rekordu przez wielu użytkowników .... W szczególności uznałem, że jest on użyteczny przy tworzeniu singletonów ...: )

Metoda @static: nie ma nic wspólnego z klasą lub instancją powiązaną z ... ale dla czytelności można użyć metody statycznej


5

Warto rozważyć różnicę między:

Class A:
    def foo():  # no self parameter, no decorator
        pass

i

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Zmieniło się to między python2 i python3:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Tak więc używanie @staticmethodmetod wywoływanych tylko bezpośrednio z klasy stało się opcjonalne w python3. Jeśli chcesz wywoływać je zarówno z klasy, jak i instancji, nadal musisz użyć @staticmethoddekoratora.

Pozostałe przypadki zostały dobrze uwzględnione w odpowiedzi Unutbus.


5

Metoda klasy odbiera klasę jako domyślny pierwszy argument, podobnie jak metoda instancji odbiera instancję. Jest to metoda powiązana z klasą, a nie z obiektem klasy. Ma ona dostęp do stanu klasy, ponieważ przyjmuje parametr klasy wskazujący na klasę, a nie na instancję obiektu. Może modyfikować stan klasy, który obowiązywałby we wszystkich instancjach klasy. Na przykład może modyfikować zmienną klasy, która będzie obowiązywać dla wszystkich instancji.

Z drugiej strony metoda statyczna nie otrzymuje domyślnego pierwszego argumentu w porównaniu do metod klasowych lub instancji. I nie może uzyskać dostępu ani modyfikować stanu klasy. Należy tylko do klasy, ponieważ z punktu widzenia projektowania jest to właściwy sposób. Ale pod względem funkcjonalności nie jest związana w czasie wykonywania z klasą.

jako wytyczne, użyj metod statycznych jako narzędzi, użyj metod klasowych, na przykład fabrycznie. A może zdefiniować singletona. I użyj metod instancji do modelowania stanu i zachowania instancji.

Mam nadzieję, że było jasne!


4

Mój wkład pokazuje różnicę wśród @classmethod, @staticmethodoraz metody instancji, w tym jak instancja może pośrednio wywołać @staticmethod. Ale zamiast pośrednio wywoływać a @staticmethodz instancji, uczynienie go prywatnym może być bardziej „pytoniczne”. Zdobycie czegoś z prywatnej metody nie zostało tutaj wykazane, ale jest to w zasadzie ta sama koncepcja.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

2

Metody klasowe, jak sama nazwa wskazuje, służą do wprowadzania zmian do klas, a nie obiektów. Aby wprowadzić zmiany w klasach, zmodyfikują atrybuty klas (nie atrybuty obiektowe), ponieważ w ten sposób aktualizujesz klasy. To jest powód, dla którego metody klasowe przyjmują klasę (konwencjonalnie oznaczoną przez „cls”) jako pierwszy argument.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Z drugiej strony metody statyczne są wykorzystywane do wykonywania funkcji, które nie są związane z klasą, tj. Nie odczytują ani nie zapisują zmiennych klas. Stąd metody statyczne nie przyjmują klas jako argumentów. Są one używane, aby klasy mogły wykonywać funkcje niezwiązane bezpośrednio z celem klasy.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

2

Analizuj @staticmethod dosłownie dostarczając różnych informacji.

Normalna metoda klasy jest domyślną metodą dynamiczną , która przyjmuje instancję jako pierwszy argument.
W przeciwieństwie do tego metoda statyczna nie przyjmuje instancji jako pierwszego argumentu, dlatego nazywa się ją „statyczna” .

Metoda statyczna jest rzeczywiście taką normalną funkcją, jak te poza definicją klasy.
Na szczęście jest zgrupowane w klasie tylko po to, aby stać bliżej miejsca zastosowania lub możesz przewinąć, aby go znaleźć.


2

Myślę, że dając czysto Pythonową wersję staticmethodiclassmethod pomoże zrozumieć różnicę między nimi na poziomie języka.

Oba są deskryptorami innymi niż dane (łatwiej byłoby je zrozumieć, jeśli najpierw znasz deskryptory ).

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner

1

staticmethod nie ma dostępu do atrybutów obiektu, klasy ani klas nadrzędnych w hierarchii dziedziczenia. Można go wywołać bezpośrednio w klasie (bez tworzenia obiektu).

classmethod nie ma dostępu do atrybutów obiektu. Może jednak uzyskać dostęp do atrybutów klasy i klas nadrzędnych w hierarchii dziedziczenia. Można go wywołać bezpośrednio w klasie (bez tworzenia obiektu). Wywołany w obiekcie jest taki sam, jak normalna metoda, która nie self.<attribute(s)>uzyskuje dostępu i dostępuself.__class__.<attribute(s)> tylko .

Pomyśl, że mamy klasę b=2, stworzymy obiekt i ponownie w nim ustawimy b=4. Metoda statyczna nie ma dostępu do niczego z poprzedniego. Classmethod może uzyskać dostęp .b==2tylko poprzez cls.b. Normalna metoda może uzyskać dostęp zarówno: .b==4przez, jak self.bi .b==2przez self.__class__.b.

Możemy postępować zgodnie ze stylem KISS (upraszczając, głupio): nie używaj metod static i metod, nie używaj klas bez ich tworzenia, uzyskuj dostęp tylko do atrybutów obiektu self.attribute(s). Istnieją języki, w których OOP jest wdrażany w ten sposób i myślę, że nie jest to zły pomysł. :)


Jeszcze jedna ważna rzecz dla metod klas: jeśli zmodyfikujesz atrybut w metodzie klasy, wszystkie istniejące obiekty tej klasy, które nie ustawią jawnie tego atrybutu, będą miały zmodyfikowaną wartość.
mirek

-4

Szybki hack innych identycznych metod w iPython ujawnia, że @staticmethoddaje marginalny wzrost wydajności (w nanosekundach), ale poza tym wydaje się, że nie spełnia żadnej funkcji. Ponadto wszelkie przyrosty wydajności zostaną prawdopodobnie usunięte przez dodatkowe prace związane z przetwarzaniem metodystaticmethod() podczas kompilacji (co dzieje się przed wykonaniem kodu po uruchomieniu skryptu).

Ze względu na czytelność kodu unikałbym, @staticmethodchyba że twoja metoda zostanie wykorzystana do prac, w których liczą się nanosekundy.


7
„W przeciwnym razie wydaje się, że nie spełnia żadnej funkcji”: nie do końca prawda. Zobacz powyższą dyskusję.
Keith Pinson,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.