Pregunta ¿Cuál es la diferencia entre @staticmethod y @classmethod en Python?


¿Cuál es la diferencia entre una función decorada con @staticmethod y uno decorado con @classmethod?


2671
2017-09-25 21:01


origen


Respuestas:


Tal vez un poco de código de ejemplo ayude: Observe la diferencia en las firmas de llamada de foo, class_foo y static_foo:

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)

    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)"%(cls,x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)"%x    

a=A()

A continuación se muestra la forma habitual en que una instancia de objeto llama a un método. La instancia del objeto a, se pasa implícitamente como el primer argumento.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

Con classmethods, la clase de la instancia del objeto se pasa implícitamente como el primer argumento en lugar de self.

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

También puedes llamar class_foo usando la clase. De hecho, si defines algo para ser un método de clase, es probablemente porque tiene la intención de llamarlo desde la clase en lugar de hacerlo desde una instancia de clase. A.foo(1) habría levantado un TypeError, pero A.class_foo(1) funciona bien

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Un uso que las personas han encontrado para los métodos de clase es crear constructores alternativos heredables.


Con métodos estáticos, ninguno self (la instancia del objeto) ni cls (la clase) se pasa implícitamente como el primer argumento. Se comportan como funciones simples, excepto que puede llamarlas desde una instancia o clase:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Los métodos estáticos se usan para agrupar funciones que tienen alguna conexión lógica con una clase a la clase.


foo es solo una función, pero cuando llamas a.foo no solo obtienes la función, obtienes una versión de la función "parcialmente aplicada" con la instancia del objeto a vinculado como el primer argumento de la función. foo espera 2 argumentos, mientras a.foo solo espera 1 argumento

a está obligado a foo. Eso es lo que significa el término "obligado" a continuación:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Con a.class_foo, a no está obligado a class_foo, más bien la clase A está obligado a class_foo.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Aquí, con un método estático, aunque sea un método, a.static_foo solo regresa una buena función 'ole' sin ningún límite de argumentos. static_foo espera 1 argumento, y a.static_foo espera 1 argumento también

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

Y, por supuesto, sucede lo mismo cuando llamas static_foo con la clase A en lugar.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

2316
2017-11-03 19:13



UN método estático es un método que no sabe nada sobre la clase o instancia a la que fue llamado. Simplemente obtiene los argumentos que se aprobaron, ningún primer argumento implícito. Básicamente es inútil en Python: solo puede usar una función de módulo en lugar de un método estático.

UN classmethod, por otro lado, es un método que pasa la clase a la que se llamó, o la clase de la instancia en la que se invocó, como primer argumento. Esto es útil cuando se quiere que el método sea una fábrica para la clase: dado que recibe la clase real a la que se llamó como primer argumento, siempre se puede instanciar la clase correcta, incluso cuando se trata de subclases. Observe por ejemplo cómo dict.fromkeys(), classmethod, devuelve una instancia de la subclase cuando se llama a una subclase:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 

654
2017-09-25 21:05



Básicamente @classmethod crea un método cuyo primer argumento es la clase desde la que se llama (en lugar de la instancia de clase), @staticmethodno tiene ningún argumento implícito


110
2017-09-25 21:07



Documentos oficiales de Python:

@classmethod

Un método de clase recibe la clase como   Primer argumento implícito, como un   el método de instancia recibe la instancia.   Para declarar un método de clase, usa esto   idioma:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 

los @classmethod la forma es una función    decorador - ver la descripción de   definiciones de funciones en Función   definiciones para detalles.

Se puede llamar en la clase   (como C.f()) o en una instancia   (como C().f()) La instancia es   ignorado a excepción de su clase. Si un   método de clase se llama para un derivado   clase, el objeto de clase derivado es   pasado como el primer argumento implícito.

Los métodos de clase son diferentes a C ++   o métodos estáticos de Java. Si tu quieres   esos, ver staticmethod() en esto   sección.

@staticmethod

Un método estático no recibe un   primer argumento implícito Para declarar un   método estático, use este modismo:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 

los @staticmethod la forma es una función    decorador - ver la descripción de   definiciones de funciones en Función   definiciones para detalles.

Se puede llamar en la clase   (como C.f()) o en una instancia   (como C().f()) La instancia es   ignorado a excepción de su clase.

Los métodos estáticos en Python son similares   a los que se encuentran en Java o C ++. Para   concepto más avanzado, ver    classmethod() en esta sección.


76
2017-11-03 19:23



aquí es un breve artículo sobre esta pregunta

La función @staticmethod no es más que una función definida dentro de una clase. Se puede llamar sin instanciar primero la clase. Su definición es inmutable a través de la herencia.

La función @classmethod también se puede llamar sin instanciar la clase, pero su definición sigue a la Subclase, no a la clase Parent, a través de la herencia. Esto se debe a que el primer argumento para @classmethod function siempre debe ser cls (clase).


55
2017-11-03 19:02



Para decidir si usar @staticmethod o @classmethod tienes que mirar dentro de tu método. Si su método accede a otras variables / métodos en su clase, utilice @classmethod. Por otro lado, si su método no toca ninguna otra parte de la clase, utilice @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

44
2018-04-22 15:40



¿Cuál es la diferencia entre @staticmethod y @classmethod en Python?

Es posible que haya visto un código de Python como este pseudocódigo, que demuestra las firmas de los diversos tipos de métodos y proporciona una docstring para explicar cada uno:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

El método de instancia normal

Primero explicaré a_normal_instance_method. Esto es precisamente llamado un "método de instancia". Cuando se utiliza un método de instancia, se usa como una función parcial (en oposición a una función total, definida para todos los valores cuando se ve en el código fuente), es decir, cuando se usa, el primero de los argumentos está predefinido como la instancia del objeto, con todos sus atributos dados. Tiene la instancia del objeto vinculado a él, y debe invocarse desde una instancia del objeto. Normalmente, accederá a varios atributos de la instancia.

Por ejemplo, esta es una instancia de una cadena:

', '

si usamos el método de instancia, join en esta cadena, para unir otra iterable, obviamente es una función de la instancia, además de ser una función de la lista iterable, ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Métodos enlazados

Los métodos de instancia pueden vincularse a través de una búsqueda punteada para su uso posterior.

Por ejemplo, esto une el str.join método a la ':' ejemplo:

>>> join_with_colons = ':'.join 

Y luego podemos usar esto como una función que ya tiene el primer argumento vinculado a él. De esta manera, funciona como una función parcial en la instancia:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Método estático

El método estático no no toma la instancia como argumento.

Es muy similar a una función de nivel de módulo.

Sin embargo, una función de nivel de módulo debe vivir en el módulo e importarse especialmente a otros lugares donde se utiliza.

Sin embargo, si está unido al objeto, también seguirá al objeto convenientemente mediante la importación y la herencia.

Un ejemplo de un método estático es str.maketrans, movido de la string módulo en Python 3. Hace una tabla de traducción adecuada para el consumo por str.translate. Parece bastante tonto cuando se usa desde una instancia de una cadena, como se demuestra a continuación, pero importando la función del string módulo es bastante torpe, y es bueno poder llamarlo desde la clase, como en str.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

En python 2, tiene que importar esta función desde el módulo de cadena cada vez menos útil:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Método de clase

Un método de clase es similar a un método de instancia en que toma un primer argumento implícito, pero en lugar de tomar la instancia, toma la clase. Con frecuencia, estos se utilizan como constructores alternativos para un mejor uso semántico y admiten la herencia.

El ejemplo más canónico de un método de clase incorporado es dict.fromkeys. Se usa como un constructor alternativo de dict (muy adecuado para cuando sabes cuáles son tus claves y quieres un valor predeterminado para ellas).

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

Cuando subclasamos dict, podemos usar el mismo constructor, que crea una instancia de la subclase.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Ver el código fuente de pandas para otros ejemplos similares de constructores alternativos, y vea también la documentación oficial de Python en classmethod y staticmethod.


38
2018-01-23 20:01



@decorators se agregaron en python 2.4. Si está usando python <2.4, puede usar la función classmethod () y staticmethod ().

Por ejemplo, si quiere crear un método de fábrica (una función que devuelve una instancia de una implementación diferente de una clase dependiendo de qué argumento obtiene) puede hacer algo como:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

También observe que este es un buen ejemplo para usar un método de clase y un método estático, El método estático pertenece claramente a la clase, ya que utiliza la clase Cluster internamente. El método de clase solo necesita información sobre la clase y ninguna instancia del objeto.

Otro beneficio de hacer el _is_cluster_for método un método de clase es para que una subclase pueda decidir cambiar su implementación, tal vez porque es bastante genérica y puede manejar más de un tipo de clúster, por lo que no bastará con verificar el nombre de la clase.


27
2018-02-24 09:32



Creo que una mejor pregunta es "¿Cuándo usarías @classmethod vs @staticmethod?"

@classmethod le permite un fácil acceso a miembros privados que están asociados a la definición de clase. esta es una gran manera de hacer singletons, o existen clases de fábrica que controlan el número de instancias de los objetos creados.

@staticmethod proporciona ganancias de rendimiento marginales, pero aún no he visto un uso productivo de un método estático dentro de una clase que no se podría lograr como una función independiente fuera de la clase.


26
2018-05-19 15:27



Métodos estáticos:

  • Funciones simples sin auto argumento.
  • Trabajar en los atributos de clase; no en atributos de instancia.
  • Se puede invocar a través de clase e instancia.
  • La función incorporada staticmethod () se usa para crearlos.

Beneficios de los métodos estáticos:

  • Localiza el nombre de la función en el classscope
  • Mueve el código de la función más cerca de donde se usa
  • Más conveniente para importar en comparación con las funciones a nivel de módulo ya que cada método no tiene que ser importado especialmente

    @staticmethod
    def some_static_method(*args, **kwds):
        pass
    

Métodos de clase:

  • Funciones que tienen primer argumento como nombre de clase.
  • Se puede invocar a través de clase e instancia.
  • Estos se crean con la función incorporada classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
    

23
2017-10-03 10:41



@staticmethod simplemente deshabilita la función predeterminada como descriptor de método. classmethod ajusta su función en un contenedor invocable que pasa una referencia a la clase propietaria como primer argumento:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

Como una cuestión de hecho, classmethod tiene una sobrecarga de tiempo de ejecución, pero hace posible acceder a la clase propietaria. Alternativamente, recomiendo usar una metaclase y poner los métodos de clase en esa metaclase:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

19
2017-09-25 21:24