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


Jaka jest różnica między funkcją ozdobioną @staticmethod i jeden ozdobiony @classmethod?


2675
2017-09-25 21:01


pochodzenie


metody statyczne są czasami lepsze jako funkcje na poziomie modułów 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ęcej zastosowań, ponieważ mogą być używane w połączeniu z polimorfizmem do tworzenia funkcji "wzorców fabrycznych". dzieje się tak, ponieważ metody klasy otrzymują klasę jako niejawny parametr. - FistOfFury
tl; dr >> w porównaniu do normalnych metod, statyczne metody i metody klas mogą być również dostępne za pomocą klasy, ale w przeciwieństwie do metod klasowych, metody statyczne są niezmienne przez dziedziczenie. - imsrgadich


Odpowiedzi:


Może trochę przykładowy kod pomoże: Zauważ różnicę w sygnaturach połączeń foo, class_foo i 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, ajest domyślnie przekazywany jako pierwszy argument.

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

Z klasowymi metodami, klasa instancji obiektu jest niejawnie przekazywana jako pierwszy argument zamiast self.

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

Możesz również zadzwonić class_foo używając klasy. W rzeczywistości, jeśli zdefiniujesz coś, co ma być jest to metoda klasowa, prawdopodobnie dlatego, że zamierzasz ją wywołać z klasy, a nie z instancji klasy. A.foo(1) podniosłoby TypeError, ale A.class_foo(1) działa dobrze:

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

Jednym z zastosowań, jakie znaleźli ludzie dla metod klasowych, jest tworzenie dziedziczący alternatywni konstruktorzy.


Metodami statycznymi, ani self (instancja obiektu) ani cls (klasa) jest domyślnie przekazywana jako pierwszy argument. Zachowują się jak zwykłe funkcje, z tym wyjątkiem, że możesz je wywołać 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ą dla klasy.


foo to tylko funkcja, ale kiedy dzwonisz a.foo nie tylko otrzymujesz funkcję, otrzymujesz "częściowo zastosowaną" wersję funkcji z instancją obiektu a związany jako pierwszy argument funkcji. foo oczekuje 2 argumentów, podczas gdy a.foo oczekuje tylko 1 argumentu.

a jest związany foo. To właśnie rozumie się przez termin "związany" poniżej:

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

Z a.class_foo, a nie jest związany class_fooraczej klasa A jest związany class_foo.

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

Tutaj, za pomocą metody statycznej, mimo że jest to metoda, a.static_foo po prostu wraca dobra funkcja "ole" bez żadnych argumentów. static_foo oczekuje 1 argumentu i a.static_foo oczekuje również 1 argumentu.

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

I oczywiście to samo dzieje się, gdy dzwonisz static_foo z klasą A zamiast.

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

2320
2017-11-03 19:13



Nie rozumiem, o co chodzi w używaniu metody statycznej. możemy po prostu użyć prostej funkcji poza klasą. - Alcott
@Alcott: Możesz przenieść funkcję do klasy, ponieważ logicznie należy ona do klasy. W kodzie źródłowym Pythona (np. Wieloprocesorowość, żółw, dist-pakiety), jest on używany do "ukrywania" pojedynczych podkreśleń "prywatnych" funkcji z przestrzeni nazw modułu. Jego użycie jest jednak mocno skoncentrowane w zaledwie kilku modułach - być może wskazówką, że jest to głównie kwestia stylistyczna. Chociaż nie mogłem znaleźć żadnego przykładu, @staticmethod może pomóc w uporządkowaniu kodu poprzez zastąpienie go podklasami. Bez niego w obszarze nazw modułu byłyby warianty funkcji. - unutbu
... wraz z wyjaśnieniem gdzie i czemu używać zarówno instancji, klas, jak i metod statycznych. Nie powiedziałeś ani słowa na ten temat, ale też PO nie zapytał o to. - MestreLion
@Alcott: jak powiedział unutbu, statyczne metody 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 nie ma sensu "zanieczyszczać" modułu wieloma "darmowymi funkcjami", a lepiej jest używać statycznego metoda polegająca na poleganiu na kiepskim stylu mieszania klas i funkcji w kodzie, aby pokazać, że są "powiązane" - MestreLion
@unutbu: Powinieneś jednak usunąć ten cytat z Guido: pochodziło to z wczesnej wersji wersji Pythona 2.2 informacji o wersji i zostało zmienione w 2.2.3 na: However, class methods are still useful in other places, for example, to program inheritable alternate constructors. Nawet oryginalny cytat jest bardzo mylący, ponieważ powiedział tylko, że nie używa go dystrybucja Pythona classmethod. Ale ponieważ sam Python nie używa danej funkcji, nie oznacza to, że jest to bezużyteczna funkcja. Myśleć o Liczby zespolone lub nawet sqrt: sam pyton nie może używać, ale nie jest bezużyteczny do zaoferowania - MestreLion


ZA staticmethod jest metodą, która nie wie nic o klasie lub instancji, do której została wywołana. Dostaje tylko argumenty, które zostały przekazane, bez domyślnego pierwszego argumentu. Zasadniczo jest to bezużyteczne w Pythonie - możesz użyć funkcji modułu zamiast statycznego.

ZA classmethodZ drugiej strony jest to metoda, która jest przekazywana do klasy, do której została wywołana, lub klasa instancji, do której została wywołana, 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ć instancję odpowiedniej klasy, nawet jeśli dotyczy ona podklas. Obserwuj na przykład, jak dict.fromkeys(), classmethod, zwraca instancję podklasy po wywołaniu na podklasę:

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

654
2017-09-25 21:05



Metoda statyczna nie jest bezużyteczna - jest to metoda umieszczenia funkcji w klasie (ponieważ logicznie tam jest), wskazując jednocześnie, że nie wymaga ona dostępu do klasy. - Tony Meyer
Stąd tylko "w zasadzie" bezużyteczne. Taka organizacja, jak również zastrzyk zależności, są poprawnymi zastosowaniami metod statycznych, ale ponieważ moduły, a nie klasy takie jak w Javie, są podstawowymi elementami organizacji kodu w Pythonie, ich użycie i użyteczność jest rzadkością. - Thomas Wouters
Co logiczne jest w definiowaniu metody wewnątrz klasy, gdy nie ma ona nic wspólnego ani z klasą, ani z jej instancjami? - Ben James
Być może ze względu na dziedziczenie? Metody statyczne mogą być dziedziczone i nadpisywane, podobnie jak metody instancji i metody klasy, a wyszukiwanie działa zgodnie z oczekiwaniami (w przeciwieństwie do Javy). Metody statyczne nie są tak naprawdę statycznie rozdzielane, czy są wywoływane w klasie czy instancji, więc jedyną różnicą między metodami klasowymi i statycznymi jest domyślny pierwszy argument. - haridsv
Tworzą także czystszy obszar nazw i ułatwiają zrozumienie, że funkcja ma coś wspólnego z klasą. - Imbrondir


Gruntownie @classmethod tworzy metodę, której pierwszym argumentem jest klasa, z której jest wywoływana (a nie instancja klasy), @staticmethodnie ma żadnych niejawnych argumentów.


110
2017-09-25 21:07





Oficjalne dokumenty Pythona:

@classmethod

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

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

The @classmethod Forma jest funkcją    dekorator - patrz opis   definicje funkcji w Funkcjonować   definicje dla szczegółów.

Można go nazwać albo na zajęciach   (Jak na przykład C.f()) lub na instancji   (Jak na przykład C().f()). Ta instancja jest   ignorowane z wyjątkiem swojej klasy. Jeśli   Metoda klasy jest wywoływana dla wyprowadzenia   klasa, obiektem klasy pochodnej jest   przekazany jako domyślny pierwszy argument.

Metody klasy różnią się od C ++   lub statyczne metody Java. Jeśli chcesz   te, patrz staticmethod() w tym   Sekcja.

@staticmethod

Metoda statyczna nie otrzymuje wartości   domyślny pierwszy argument. Aby zadeklarować   metoda statyczna, użyj tego idiomu:

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

The @staticmethod Forma jest funkcją    dekorator - patrz opis   definicje funkcji w Funkcjonować   definicje dla szczegółów.

Można go nazwać albo na zajęciach   (Jak na przykład C.f()) lub na instancji   (Jak na przykład C().f()). Ta instancja jest   ignorowane z wyjątkiem swojej klasy.

Metody statyczne w Pythonie są podobne   do tych znalezionych w Javie lub C ++. Dla   bardziej zaawansowana koncepcja, patrz    classmethod() w tej sekcji.


76
2017-11-03 19:23





Tutaj to krótki artykuł na ten temat

Funkcja @staticmethod jest niczym więcej niż funkcją zdefiniowaną w klasie. Można go wywołać bez tworzenia pierwszej instancji klasy. Jego definicja jest niezmienna przez dziedziczenie.

Funkcja @classmethod jest również wywoływalna bez tworzenia instancji klasy, ale jej definicja wynika z klasy Sub, a nie klasy Parent, poprzez dziedziczenie. A to dlatego, że pierwszym argumentem funkcji @classmethod musi być zawsze cls (klasa).


55
2017-11-03 19:02



Czy to oznacza, że ​​używając metody statycznej, jestem zawsze związany z klasą Parent i classmethod, jestem związany klasą, w której deklaruję metodę class (w tym przypadku podklasę)? - Mohan Gulati
Nie. Używając metody statycznej, nie jesteś w żaden sposób związany; nie ma domyślnego pierwszego parametru. Używając metody classmethod, dostajesz jako niejawny pierwszy parametr w klasie, do której wywołałeś metodę (jeśli wywołałeś ją bezpośrednio w klasie), lub klasę instancji, którą wywołałeś (jeśli wywołałeś ją na instancji). - Matt Anderson
Można go nieco rozszerzyć, 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 (będą musiały zakodować na stałe MyClass.attr) - MestreLion


Aby zdecydować, czy użyć @staticmethod lub @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

44
2018-04-22 15:40



Szkoda, że ​​Twój przykład statycznej metody faktycznie robi uzyskać dostęp do zmiennej klasy. Możesz poprawić swój przykład. - Cilyan


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

Być może widzieliście kod Pythona taki jak ten pseudokod, który demonstruje podpisy różnych typów metod i dostarcza objaśnienie każdego 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 normalnej instancji

Najpierw wyjaśnię a_normal_instance_method. Jest to dokładnie nazywane "metoda instancjiGdy 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 wyświetlanych w kodzie źródłowym), która jest używana, gdy pierwszy z argumentów jest predefiniowany jako instancja obiektu, ze wszystkimi jego atrybutami, posiada instancję obiektu, do którego jest przypisany, i musi być wywołana z instancji obiektu, zwykle uzyskuje dostęp do różnych atrybutów instancji.

Na przykład jest to ciąg znaków:

', '

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

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

Metody związane

Metody instancji można powiązać za pomocą wyszukiwania wykropkowanego w celu późniejszego użycia.

Na przykład wiąże to str.join metoda do ':' instancja:

>>> join_with_colons = ':'.join 

A później możemy użyć tego jako funkcji, która ma już pierwszy związany z nią argument. W ten sposób działa jak częściowa funkcja na 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 ma nie potraktuj instancję jako argument.

Jest bardzo podobny do funkcji na poziomie modułu.

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

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

Przykładem metody statycznej jest str.maketrans, przeniesiony z string moduł w Pythonie 3. Tworzy tabelę tłumaczeń odpowiednią do wykorzystania przez str.translate. Wydaje się raczej głupie, gdy jest używany z instancją ciąg, jak pokazano poniżej, ale importowanie funkcji z string Moduł jest raczej niezgrabny i miło jest móc go wywołać z klasy, jak w str.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 string:

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

Metoda klasy

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

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

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

Kiedy używamy klasy podklasy, 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 i zobacz także oficjalną dokumentację Pythona na classmethod i staticmethod.


38
2018-01-23 20:01





@decorators zostały dodane w python 2.4 Jeśli używasz pytona <2.4, możesz użyć funkcji classmethod () i staticmethod ().

Na przykład, jeśli chcesz utworzyć metodę fabryczną (funkcja zwracająca instancję o innej implementacji klasy w zależności od tego, jaki argument otrzymuje), 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ż wykorzystuje wewnętrznie klaster klasy. Metoda class potrzebuje tylko informacji o klasie i braku instancji obiektu.

Kolejna korzyść z tworzenia _is_cluster_for method a classmethod jest tak, że podklasa może zdecydować o zmianie jej implementacji, może dlatego, że jest dość ogólna i może obsłużyć więcej niż jeden typ klastra, więc samo sprawdzenie nazwy klasy nie byłoby wystarczające.


27
2018-02-24 09:32