Quando dovrei usare @classmethod e quando def method (self)?


92

Durante l'integrazione di un'app Django che non ho usato prima, ho trovato due modi diversi utilizzati per definire le funzioni nelle classi. L'autore sembra usarli entrambi molto intenzionalmente. Il primo è quello che io stesso uso molto:

class Dummy(object):

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

L'altro è quello che non uso, soprattutto perché non capisco quando usarlo e per cosa:

class Dummy(object):

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

Nella documentazione Python il classmethoddecoratore viene spiegato con questa frase:

Un metodo di classe riceve la classe come primo argomento implicito, proprio come un metodo di istanza riceve l'istanza.

Quindi immagino si clsriferisca a Dummyse stesso (il class, non l'istanza). Non capisco esattamente perché questo esista, perché potrei sempre farlo:

type(self).do_something_with_the_class

È solo per motivi di chiarezza o mi sono perso la parte più importante: cose inquietanti e affascinanti che non si potrebbero fare senza di essa?

Risposte:


71

La tua ipotesi è corretta: capisci come classmethod funziona.

Il motivo è che questi metodi possono essere chiamati sia su un'istanza che sulla classe (in entrambi i casi, l'oggetto classe verrà passato come primo argomento):

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

Sull'uso di questi sulle istanze : ci sono almeno due usi principali per chiamare un metodo di classe su un'istanza:

  1. self.some_function()chiamerà la versione di some_functionsul tipo effettivo di self, piuttosto che la classe in cui appare quella chiamata (e non avrà bisogno di attenzione se la classe viene rinominata); e
  2. Nei casi in cui some_functionè necessario implementare qualche protocollo, ma è utile chiamare solo l'oggetto classe.

La differenza constaticmethod : esiste un altro modo per definire i metodi che non accedono ai dati dell'istanza, chiamato staticmethod. Ciò crea un metodo che non riceve affatto un primo argomento implicito; di conseguenza non verrà passata alcuna informazione sull'istanza o classe su cui è stata chiamata.

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

L'uso principale che ho trovato è quello di adattare una funzione esistente (che non si aspetta di ricevere a self) come metodo su una classe (o oggetto).


2
Bello: mi piace che la risposta sia suddivisa in come - perché. Per quanto ho capito ora, @classmethod consente di accedere alla funzione senza la necessità di un'istanza. Questo è esattamente quello che stavo cercando, grazie.
Marue

1
@marcin La vera dattilografia gli conferisce un'altra dimensione. Si trattava più di non scrivere cose come self.foo()quando dovrebbe essere Bar.foo().
Voo

5
@Voo In realtà, self.foo()è preferibile, perché selfpotrebbe essere un'istanza di una sottoclasse che implementa la propria foo.
Marcin

1
@ Marcin Penso che questo dipenda da cosa vuoi ottenere. Ma ho capito il tuo punto.
marue

1
Dovresti usare un lambda diverso per Bar, as 1+1 == 2 == 1*2, quindi è impossibile dire dai risultati mostrati che Bar().static_methodè effettivamente chiamato.
George V. Reilly

0

Fondamentalmente, dovresti usare un @classmethod quando ti rendi conto che la definizione del metodo non verrà modificata o sovrascritta.

Un ulteriore: teoricamente, i metodi di classe sono più veloci dei metodi oggetto, perché non hanno bisogno di essere istanziati e richiedono meno memoria.


-3

Se aggiungi decorator @classmethod, significa che farai quel metodo come metodo statico di java o C ++. (il metodo statico è un termine generico immagino ;) ) Python ha anche @staticmethod. e la differenza tra classmethod e staticmethod è se puoi accedere alla classe o alla variabile statica usando l'argomento o il nome classe stesso.

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

tutte quelle classi aumentano cls.cls_var di 1 e lo stampano.

E ogni classe che utilizza lo stesso nome sullo stesso ambito o istanze costruite con queste classi condividerà questi metodi. C'è solo un TestMethod.cls_var e c'è anche un solo TestMethod.class_method (), TestMethod.static_method ()

E una domanda importante. perché sarebbe necessario questo metodo.

classmethod o staticmethod è utile quando si crea quella classe come factory o quando è necessario inizializzare la classe solo una volta. come aprire il file una volta e utilizzando il metodo feed per leggere il file riga per riga.


2
(a) I metodi statici sono qualcos'altro; (b) i metodi di classe non sono condivisi da tutte le classi.
Marcin

@ Marcin grazie per avermi fatto conoscere le mie definizioni poco chiare e tutto il resto.
Ryan Kim
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.