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


88

Al integrar una aplicación de Django que no había usado antes, encontré dos formas diferentes de definir funciones en clases. El autor parece usarlos a ambos muy 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 classmethoddecorador 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 se clsrefiere a Dummysí mismo (la class, no la instancia). No entiendo exactamente por qué existe esto, porque siempre podría hacer esto:

type(self).do_something_with_the_class

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

Respuestas:


70

Tu suposición es correcta, entiendes cómo classmethod funcionan los s.

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

Sobre el uso de estos en instancias : 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 del some_functiontipo real deself , en lugar de 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 que some_functiones necesario implementar algún protocolo, pero es útil llamar al objeto de clase solo.

La diferencia constaticmethod : Hay otra forma de definir métodos que no acceden a datos de instancia, denominada 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 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 uso principal que le he encontrado es adaptar una función existente (que no espera recibir a self) para ser un método en una clase (u objeto).


2
Agradable: me gusta que la respuesta se divida en cómo y por qué. Por lo que tengo ahora, @classmethod permite acceder a la función sin la necesidad de una instancia. Esto es exactamente lo que estaba buscando, gracias.
marue

1
@marcin El tipeo de pato verdadero le da otra dimensión. Se trataba más de no escribir cosas como self.foo()debería ser Bar.foo().
Voo

5
@Voo En realidad, self.foo()es preferible, porque selfpuede ser una instancia de una subclase que implementa la suya propia foo.
Marcin

1
@Marcin Creo que esto depende de lo que quieras lograr. Pero entiendo tu punto.
marue

1
Debe usar una lambda diferente para Bar, as 1+1 == 2 == 1*2, por lo que es imposible saber a partir de los resultados mostrados que Bar().static_methodse llama realmente.
George V. Reilly

0

Básicamente, debe usar un @classmethod cuando se dé cuenta de que la definición del método no se cambiará ni se anulará.

Un adicional: teóricamente, los métodos de clase son más rápidos que los métodos de objeto, porque no necesitan ser instanciados y necesitan menos memoria.


-3

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

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 y lo imprimen.

Y todas las clases que usan el mismo nombre en el mismo alcance 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 pregunta importante. por qué se necesitarían estos métodos.

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


2
(a) Los métodos estáticos son otra cosa; (b) los métodos de clase no son compartidos por todas las clases.
Marcin

@Marcin, gracias por dejarme saber mis definiciones poco claras y todo eso.
Ryan Kim
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.