¿Cuándo debo usar @classmethod y cuando def method (self)?

Al integrar una aplicación Django que no he usado antes, encontré dos formas diferentes que se usan para definir funciones en las clases. El autor parece usarlos ambos intencionalmente. El primero es uno que yo mismo uso mucho:

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

El otro es uno que no uso, principalmente porque no entiendo cuándo usarlo y para qué.

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

En los documentos de Python, el decorador de classmethod se explica con esta oración:

Un método de clase recibe la clase como primer argumento implícito, al igual que un método de instancia recibe la instancia.

Así que supongo que cls refiere a Dummy sí (la class , no la instancia). No entiendo exactamente por qué esto existe, porque siempre podría hacer esto:

 type(self).do_something_with_the_class 

¿Es esto solo por razones de claridad, o me perdí la parte más importante: cosas espeluznantes y fascinantes que no se podrían hacer sin ella?

Tu suposición es correcta: entiendes cómo funciona el classmethod .

El por qué es que estos métodos se pueden llamar tanto en una instancia O en la clase (en ambos casos, el objeto de la clase se pasará como el primer 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() 

En el uso de estos en casos : hay al menos dos usos principales para llamar a un método de clase en una instancia:

  1. self.some_function() llamará a la versión de some_function en el tipo real de self , en lugar de a la clase en la que aparece esa llamada (y no necesitará atención si se cambia el nombre de la clase); y
  2. En los casos en los que some_function es necesaria para implementar algún protocolo, pero es útil para llamar solo al objeto de clase.

La diferencia con el staticmethod : existe otra forma de definir métodos que no acceden a los datos de instancia, llamado staticmethod . Eso crea un método que no recibe un primer argumento implícito en absoluto; en consecuencia, no se pasará ninguna información sobre la instancia o la clase en la que se llamó.

 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 

El principal uso que he encontrado es adaptar una función existente (que no espera recibir un self ) para que sea un método en una clase (u objeto).

Si agrega el decorador @classmethod, eso significa que va a hacer ese método como método estático de java o C ++. (Método estático es un término general , supongo 😉 ) Python también tiene @staticmethod. y la diferencia entre el método de clase y el método estático es si puede acceder a la clase o variable estática utilizando el argumento o el propio nombre de clase.

 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 esas clases aumentan cls.cls_var en 1 e imprimen.

Y todas las clases que utilicen el mismo nombre en el mismo ámbito o instancias construidas con estas clases compartirán esos métodos. Solo hay un TestMethod.cls_var y también solo hay un TestMethod.class_method (), TestMethod.static_method ()

Y la pregunta importante. Por qué se necesitarían estos métodos.

El método de clase o método estático es útil cuando crea esa clase como una fábrica o cuando tiene que inicializar su clase solo una vez. Me gusta abrir el archivo una vez y usar el método de alimentación para leer el archivo línea por línea.