Quando devo usar @classmethod e quando def method (self)?

88

Ao integrar um aplicativo Django que não usei antes, descobri duas maneiras diferentes de definir funções em classes. O autor parece usar os dois muito intencionalmente. O primeiro é um que eu mesmo uso muito:

class Dummy(object):

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

O outro é um que não uso, principalmente porque não entendo quando usá-lo e para que:

class Dummy(object):

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

Nos documentos Python, o classmethoddecorador é explicado com esta frase:

Um método de classe recebe a classe como primeiro argumento implícito, assim como um método de instância recebe a instância.

Então eu acho que clsse refere a Dummysi mesmo (a class, não a instância). Eu não entendo exatamente porque isso existe, porque eu sempre poderia fazer isso:

type(self).do_something_with_the_class

É apenas por uma questão de clareza ou perdi a parte mais importante: coisas assustadoras e fascinantes que não poderiam ser feitas sem ele?

Marue
fonte

Respostas:

70

Seu palpite está correto - você entende como classmethod funciona.

O motivo é que esses métodos podem ser chamados em uma instância OU na classe (em ambos os casos, o objeto da classe será passado como o primeiro argumento):

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

Sobre o uso deles em instâncias : Existem pelo menos dois usos principais para chamar um método de classe em uma instância:

  1. self.some_function()chamará a versão de some_functionno tipo real de self, em vez da classe na qual essa chamada aparece (e não precisará de atenção se a classe for renomeada); e
  2. Nos casos em que some_functioné necessário implementar algum protocolo, mas é útil chamar o objeto de classe sozinho.

A diferença comstaticmethod : Existe outra maneira de definir métodos que não acessam dados da instância, chamada staticmethod. Isso cria um método que não recebe um primeiro argumento implícito; conseqüentemente, não será passada nenhuma informação sobre a instância ou classe na qual foi chamada.

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

O principal uso que encontrei para ele é adaptar uma função existente (que não espera receber um self) para ser um método em uma classe (ou objeto).

Marcin
fonte
2
Boa: gosto que a resposta seja dividida em como - por quê. Pelo que entendi agora, @classmethod permite acessar a função sem a necessidade de uma instância. Isso é exatamente o que eu estava procurando, obrigado.
março
1
@marcin A digitação True duck lhe dá alguma outra dimensão. Era mais sobre não escrever coisas como self.foo()deveriam ser Bar.foo().
Voo
5
@Voo Na verdade, self.foo()é preferível, porque selfpode ser uma instância de uma subclasse que implementa a sua própria foo.
Marcin
1
@Marcin Acho que isso depende do que você deseja alcançar. Mas eu entendi seu ponto.
Marue
1
Você deve usar um lambda diferente para Bar, as 1+1 == 2 == 1*2, portanto, é impossível dizer a partir dos resultados mostrados que Bar().static_methodé realmente chamado.
George V. Reilly
0

Basicamente, você deve usar um método @class quando perceber que a definição do método não será alterada ou substituída.

Um adicional: teoricamente, os métodos de classe são mais rápidos que os métodos de objeto, porque não precisam ser instanciados e precisam de menos memória.

Joao barbosa
fonte
-3

Se você adicionar decorator @classmethod, isso significa que você fará esse método como um método estático de java ou C ++. (método estático é um termo geral, eu acho ;) ) Python também tem @staticmethod. e a diferença entre o método de classe e o método estático é se você pode acessar a classe ou a variável estática usando o próprio argumento ou nome da classe.

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

todas essas classes aumentam cls.cls_var em 1 e o imprimem.

E todas as classes que usam o mesmo nome no mesmo escopo ou instâncias construídas com essas classes irão compartilhar esses métodos. Há apenas um TestMethod.cls_var e também há apenas um TestMethod.class_method (), TestMethod.static_method ()

E uma pergunta importante. por que esses métodos seriam necessários.

classmethod ou staticmethod é útil quando você faz essa classe como uma fábrica ou quando você precisa inicializar sua classe apenas uma vez. como abrir o arquivo uma vez e usar o método de alimentação para ler o arquivo linha por linha.

Ryan Kim
fonte
2
(a) Os métodos estáticos são outra coisa; (b) os métodos de classe não são compartilhados por todas as classes.
Marcin
@Marcin obrigado por me deixar saber minhas definições pouco claras e tudo isso.
Ryan Kim