Kiedy należy używać metody @classmethod, a kiedy metody def (self)?

88

Podczas integracji aplikacji Django, której wcześniej nie używałem, znalazłem dwa różne sposoby definiowania funkcji w klasach. Autor wydaje się używać ich obu bardzo celowo. Pierwszy to ten, którego sam często używam:

class Dummy(object):

    def some_function(self,*args,**kwargs):
        do something here
        self is the class instance

Drugi to taki, którego nie używam, głównie dlatego, że nie rozumiem, kiedy go używać i do czego:

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        do something here
        cls refers to what?

W dokumentacji Pythona classmethoddekorator jest wyjaśniony następującym zdaniem:

Metoda klasy otrzymuje klasę jako niejawny pierwszy argument, tak jak metoda instancji otrzymuje instancję.

Więc myślę, że clsodnosi się do Dummysiebie (a classnie do instancji). Nie do końca rozumiem, dlaczego tak jest, ponieważ zawsze mogłem to zrobić:

type(self).do_something_with_the_class

Czy to tylko dla jasności, czy też przegapiłem najważniejszą część: straszne i fascynujące rzeczy, których nie można by zrobić bez tego?

marue
źródło

Odpowiedzi:

70

Twoje przypuszczenie jest poprawne - rozumiesz, jak classmethod to działa.

Powodem jest to, że te metody można wywołać zarówno w instancji LUB w klasie (w obu przypadkach obiekt klasy zostanie przekazany jako pierwszy argument):

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        print cls

#both of these will have exactly the same effect
Dummy.some_function()
Dummy().some_function()

O ich użyciu w instancjach : Istnieją co najmniej dwa główne zastosowania do wywoływania metody class w instancji:

  1. self.some_function()wywoła wersję some_functionna rzeczywistym typie self, a nie klasę, w której to wywołanie się pojawi (i nie będzie wymagało uwagi, jeśli nazwa klasy zostanie zmieniona); i
  2. W przypadkach, gdy some_functionkonieczne jest zaimplementowanie jakiegoś protokołu, ale przydatne jest wywołanie samego obiektu klasy.

Różnicastaticmethod polega na: Istnieje inny sposób definiowania metod, które nie mają dostępu do danych instancji, tzw staticmethod. To tworzy metodę, która w ogóle nie otrzymuje ukrytego pierwszego argumentu; w związku z tym nie zostaną przekazane żadne informacje o instancji lub klasie, na której została wywołana.

In [6]: class Foo(object): some_static = staticmethod(lambda x: x+1)

In [7]: Foo.some_static(1)
Out[7]: 2

In [8]: Foo().some_static(1)
Out[8]: 2

In [9]: class Bar(Foo): some_static = staticmethod(lambda x: x*2)

In [10]: Bar.some_static(1)
Out[10]: 2

In [11]: Bar().some_static(1)
Out[11]: 2

Głównym zastosowaniem, jakie znalazłem, jest przystosowanie istniejącej funkcji (która nie spodziewa się otrzymać a self), aby była metodą klasy (lub obiektu).

Marcin
źródło
2
Fajnie: podoba mi się odpowiedź na pytanie: jak - dlaczego. O ile mam to teraz, @classmethod umożliwia dostęp do funkcji bez potrzeby instancji. Właśnie tego szukałem, dziękuję.
marue
1
@marcin Pisanie prawdziwej kaczki nadaje mu inny wymiar. Chodziło bardziej o to, żeby nie pisać rzeczy, self.foo()kiedy powinno Bar.foo().
Voo
5
@Voo Właściwie self.foo()jest preferowany, ponieważ selfmoże być instancją podklasy, która implementuje własną foo.
Marcin
1
@Marcin Myślę, że to zależy od tego, co chcesz osiągnąć. Ale rozumiem.
marue
1
Powinieneś użyć innej lambda dla Bar, as 1+1 == 2 == 1*2, więc na podstawie wyświetlonych wyników nie można stwierdzić, że Bar().static_methodfaktycznie jest nazywana.
George V. Reilly,
0

Zasadniczo powinieneś użyć @classmethod, gdy zdasz sobie sprawę, że definicja metody nie zostanie zmieniona ani zastąpiona.

Dodatkowo: teorycznie metody klasowe są szybsze niż metody obiektowe, ponieważ nie wymagają tworzenia instancji i wymagają mniej pamięci.

Joao Barbosa
źródło
-3

Jeśli dodasz dekorator @classmethod, oznacza to, że zamierzasz utworzyć tę metodę jako metodę statyczną w Javie lub C ++. (Metoda statyczna jest ogólnym terminem Chyba ;) ) Pythona ma również @staticmethod. a różnica między classmethod i staticmethod polega na tym, czy możesz uzyskać dostęp do klasy lub zmiennej statycznej za pomocą argumentu lub samej nazwy klasy.

class TestMethod(object):
    cls_var = 1
    @classmethod
    def class_method(cls):
        cls.cls_var += 1
        print cls.cls_var

    @staticmethod
    def static_method():
        TestMethod.cls_var += 1
        print TestMethod.cls_var
#call each method from class itself.
TestMethod.class_method()
TestMethod.static_method()

#construct instances
testMethodInst1 = TestMethod()    
testMethodInst2 = TestMethod()   

#call each method from instances
testMethodInst1.class_method()
testMethodInst2.static_method()

wszystkie te klasy zwiększają cls.cls_var o 1 i wyświetlają go.

I wszystkie klasy używające tej samej nazwy w tym samym zakresie lub instancjach skonstruowanych z tą klasą będą współdzielić te metody. Jest tylko jeden TestMethod.cls_var, a także jest tylko jeden TestMethod.class_method (), TestMethod.static_method ()

I ważne pytanie. dlaczego ta metoda byłaby potrzebna.

classmethod lub staticmethod są przydatne, gdy tworzysz tę klasę jako fabrykę lub gdy musisz zainicjować swoją klasę tylko raz. jak raz otwórz plik i używając metody feed, aby odczytać plik wiersz po wierszu.

Ryan Kim
źródło
2
(a) Metody statyczne to coś innego; (b) metody klasowe nie są wspólne dla każdej klasy.
Marcin
@Marcin dzięki za poinformowanie mnie o moich niejasnych definicjach i tak dalej.
Ryan Kim