Pregunta

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

¿Fue útil?

Solución

Quizás 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 primer argumento.

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

Con métodos de clase, la clase de la instancia del objeto se pasa implícitamente como 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 define algo para ser un metro de clase, probablemente sea porque tiene la intención de llamarlo de la clase en lugar de una instancia de clase. A.foo(1) habría generado un TypeError, pero A.class_foo(1) funciona bien:

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

Un uso que la gente ha encontrado para los métodos de clase es crear constructores alternativos heredables.


Con métodos estáticos, ni self (la instancia del objeto) ni cls (la clase) se pasa implícitamente como primer argumento.Se comportan como funciones simples excepto que puedes 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 utilizan para agrupar funciones que tienen alguna conexión lógica con una clase.


foo es solo una función, pero cuando llamas a.foo No solo obtiene la función, obtiene una versión "parcialmente aplicada" de la función con la instancia de objeto a vinculado como primer argumento de la función. foo espera 2 argumentos, mientras a.foo sólo espera 1 argumento.

a está obligado a foo.Eso es lo que se entiende por el término "consolidado" 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 devuelve una buena función de OLE sin argumentos vinculados. static_foo espera 1 argumento, ya.static_foo también espera 1 argumento.

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

Y por supuesto pasa lo mismo cuando llamas static_foo con la clase A en cambio.

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

Otros consejos

Un método estático es un método que no sabe nada acerca de la clase o instancia en la que se invocó. Simplemente obtiene los argumentos que se aprobaron, no el primer argumento implícito. Básicamente es inútil en Python: 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 a la que se llamó, como primer argumento. Esto es útil cuando desea que el método sea una fábrica para la clase: dado que obtiene la clase real a la que se llamó como primer argumento, siempre puede crear una instancia de la clase correcta, incluso cuando están involucradas subclases. Observe, por ejemplo, cómo dict.fromkeys(), un método de clase, devuelve una instancia de la subclase cuando se llama en una subclase:

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

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

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, use esto   modismo:

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

El formulario @classmethod es una función    decorador & # 8211; ver la descripción de   definiciones de funciones en Función   definiciones para más detalles.

     

Se puede invocar 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   Se llama al método de clase 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, vea staticmethod() en este   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, ...): ... 
     

El formulario @staticmethod es una función    decorador & # 8211; ver la descripción de   definiciones de funciones en Función   definiciones para más detalles.

     

Se puede invocar en la clase   (como classmethod()) o en una instancia   (como <=>). 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    <=> en esta sección.

Aquí es un breve artículo sobre este pregunta

  

La función @staticmethod no es más que una función definida dentro de una clase. Es invocable sin instanciar la clase primero. Su & # 8217; s definición es inmutable a través de la herencia.

     La función

@classmethod también se puede llamar sin crear instancias de la clase, pero su definición sigue a la clase Sub, no a la clase Parent, a través de la herencia. Eso & # 8217; s porque el primer argumento para la función @classmethod siempre debe ser cls (clase).

Para decidir si usar @staticmethod o @classmethod debe buscar dentro de su método. Si su método accede a otras variables / métodos en su clase, use @classmethod . Por otro lado, si su método no toca ninguna otra parte de la clase, use @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
  

¿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 muestra las firmas de los distintos tipos de métodos y proporciona una cadena de documentación 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 se llama precisamente & Quot; método de instancia & Quot ;. Cuando se usa 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 vinculada a él y debe llamarse desde una instancia del objeto. Normalmente, accederá a varios atributos de la instancia.

Por ejemplo, esta es una instancia de una cadena:

', '

si utilizamos el método de instancia, join en esta cadena, para unir otro 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 vinculados

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

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

>>> join_with_colons = ':'.join 

Y luego podemos usar esto como una función que ya tiene el primer argumento vinculado. 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 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 y debe importarse especialmente a otros lugares donde se utiliza.

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

Un ejemplo de método estático es str.maketrans, movido desde el módulo string en Python 3. Hace que una tabla de traducción sea 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 importar la función desde el módulo dict.fromkeys es bastante torpe, y es bueno poder llamarlo desde la clase, como en classmethod

# 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, debe 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 admitirá la herencia.

El ejemplo más canónico de un método de clase integrado es staticmethod. Se utiliza como un constructor alternativo de dict (muy adecuado para cuando sepa cuáles son sus claves y desee un valor predeterminado para ellas).

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

Cuando subclasemos 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'>

Vea el código fuente de pandas para otros similares ejemplos de constructores alternativos, y vea también la documentación oficial de Python en <=> y <=> .

Comencé a aprender el lenguaje de programación con C++ y luego Java y luego Python, por lo que esta pregunta también me molestó mucho, hasta que entendí el uso simple de cada uno.

Método de clase: Python, a diferencia de Java y C++, no tiene sobrecarga de constructores.Y para lograr esto podrías usar classmethod.El siguiente ejemplo explicará esto.

Consideremos que tenemos un Person clase que toma dos argumentos first_name y last_name y crea la instancia de Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Ahora, si el requisito es que necesita crear una clase usando un solo nombre, solo un first_name, tú no poder haz algo como esto en Python.

Esto le dará un error cuando intente crear un objeto (instancia).

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

Sin embargo, podrías lograr lo mismo usando @classmethod Como se menciona abajo

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Método estático: Esto es bastante simple, no está vinculado a una instancia o clase y simplemente puede llamarlo usando el nombre de la clase.

Entonces digamos que en el ejemplo anterior necesitas una validación que first_name no debe exceder los 20 caracteres, simplemente puedes hacer esto.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

y simplemente podrías llamar usando class name

Person.validate_name("Gaurang Shah")

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

Por ejemplo, si desea crear un método de fábrica (una función que devuelve una instancia de una implementación diferente de una clase según el argumento que obtenga) 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)

Observe también 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 que el método _is_cluster_for sea un método de clase es que una subclase puede decidir cambiar su implementación, tal vez porque es bastante genérico y puede manejar más de un tipo de clúster, por lo que solo verificar el nombre de la clase no será suficiente.

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

@classmethod le permite acceder fácilmente a miembros privados que están asociados a la definición de clase. esta es una excelente 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 pueda lograr como una función independiente fuera de la clase.

Métodos estáticos:

  • Funciones simples sin argumento propio.
  • Trabajar en atributos de clase; no en atributos de instancia.
  • Se puede llamar a través de la clase y la 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 función más cerca de donde se usa
  • Más conveniente para importar en comparación con las funciones de nivel de módulo ya que cada método no tiene que importarse especialmente

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

Métodos de clase:

  • Funciones que tienen el primer argumento como classname.
  • Se puede llamar a través de la clase y la instancia.
  • Estos se crean con la función incorporada classmethod.

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

@staticmethod simplemente deshabilita la función predeterminada como descriptor de método. classmethod envuelve 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'>>

De hecho, classmethod tiene una sobrecarga de tiempo de ejecución pero permite 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'>

La guía definitiva sobre cómo usar métodos estáticos, de clase o abstractos en Python es un buen enlace para este tema y lo resuma de la siguiente manera.

@staticmethod La función no es más que una función definida dentro de una clase.Se puede invocar sin crear una instancia de la clase primero.Su definición es inmutable por herencia.

  • Python no tiene que crear una instancia de un método vinculado para el objeto.
  • Facilita la legibilidad del código y no depende del estado del objeto en sí;

@classmethod La función también se puede invocar sin crear una instancia de la clase, pero su definición sigue a la subclase, no a la clase principal, a través de herencia, puede ser anulada por la subclase.Esto se debe a que el primer argumento a favor @classmethod La función siempre debe ser cls (clase).

  • Métodos de fábrica, que se utilizan para crear una instancia para una clase utilizando, por ejemplo, algún tipo de preprocesamiento.
  • Métodos estáticos que llaman a métodos estáticos:Si divide un método estático en varios métodos estáticos, no debe codificar el nombre de la clase sino usar métodos de clase.

Permítanme decir la similitud entre un método decorado con @classmethod vs @staticmethod primero.

Similitud: Se puede llamar a ambos en la Clase en lugar de solo la instancia de la clase. Entonces, ambos en cierto sentido son métodos de clase .

Diferencia: un método de clase recibirá la clase como primer argumento, mientras que un método estático no lo hará.

Entonces, un método estático, en cierto sentido, no está ligado a la Clase en sí misma y simplemente está colgando allí solo porque puede tener una funcionalidad relacionada.

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

Otra consideración con respecto al método estático vs método de clase surge con la herencia. Digamos que tiene la siguiente clase:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

Y luego desea anular bar() en una clase secundaria:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Esto funciona, pero tenga en cuenta que ahora la implementación Foo2 en la clase secundaria (magic()) ya no puede aprovechar nada específico de esa clase. Por ejemplo, supongamos que Foo2.magic() tenía un método llamado Foo que desea utilizar en la implementación de classmethod de In Foo2 MAGIC:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

La solución aquí sería llamar a <=> en <=>, pero luego se repite (si cambia el nombre de <=>, deberá recordar actualizar ese método <=>).

Para mí, esto es una ligera violación del principio abierto / cerrado , ya que una decisión tomada en <=> está afectando su capacidad de refactorizar código común en una clase derivada (es decir, está menos abierto a la extensión). Si <=> fuera un <=> estaríamos bien:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

Da: <=>

Trataré de explicar la diferencia básica usando un ejemplo.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - podemos llamar directamente a métodos estáticos y de clase sin inicializar

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- El método estático no puede llamar al método self pero puede llamar a otro método estático y de clase

3- El método estático pertenece a la clase y no usará ningún objeto en absoluto.

4- El método de clase no está vinculado a un objeto sino a una clase.

@classmethod: puede usarse para crear un acceso global compartido a todas las instancias creadas de esa clase ... como actualizar un registro por múltiples usuarios ... Particularmente, también me pareció útil cuando creamos singletons ... :)

Método @static: no tiene nada que ver con la clase o instancia asociada con ... pero para facilitar la lectura puede usar el método estático

Mi contribución demuestra la diferencia entre @classmethod, @staticmethod, y métodos de instancia, incluido cómo una instancia puede llamar indirectamente a un @staticmethod.Pero en lugar de llamar indirectamente a un @staticmethod Desde un caso, hacerlo en privado puede ser más "pitónico". Obtener algo de un método privado no está demostrado aquí, pero es básicamente el mismo concepto.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

Los métodos de clase, como su nombre indica, se utilizan para realizar cambios en las clases y no en los objetos. Para realizar cambios en las clases, modificarán los atributos de la clase (no los atributos del objeto), ya que así es como se actualizan las clases. Esta es la razón por la cual los métodos de clase toman la clase (convencionalmente denotada por 'cls') como primer argumento.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Los métodos estáticos, por otro lado, se utilizan para realizar funcionalidades que no están vinculadas a la clase, es decir, no leerán ni escribirán variables de clase. Por lo tanto, los métodos estáticos no toman clases como argumentos. Se utilizan para que las clases puedan realizar funcionalidades que no están directamente relacionadas con el propósito de la clase.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

Es posible que desee considerar la diferencia entre:

Class A:
    def foo():  # no self parameter, no decorator
        pass

y

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Esto ha cambiado entre python2 y python3:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Por lo tanto, usar @staticmethod para métodos que solo se invocan directamente desde la clase se ha convertido en opcional en python3. Si desea llamarlos desde la clase y la instancia, aún necesita usar el decorador <=>.

Los otros casos han sido bien cubiertos por la respuesta unutbus.

Analice @staticmethod literalmente proporcionando diferentes puntos de vista.

Un método normal de una clase es un método implícito dinámico que toma la instancia como primer argumento.
Por el contrario, un método estático no toma la instancia como primer argumento, por lo que se denomina 'estática' .

Un método estático es, de hecho, una función tan normal como las que están fuera de una definición de clase.
Afortunadamente se agrupa en la clase solo para estar más cerca de donde se aplica, o puede desplazarse para encontrarlo.

Respuesta corta. El primer argumento:

  • método normal: el primer argumento es el objeto actual
  • classmethod: primer argumento es la clase del objeto actual
  • método estático: se elimina el primer argumento

Respuesta más larga:

método normal

Cuando se llama al método de un objeto, se le asigna automáticamente un argumento adicional self como primer argumento. Es decir, método

def f(self, x, y)

debe llamarse con 2 argumentos. classmethod se pasa automáticamente, y es el objeto mismo .

método de clase

Cuando el método está decorado

@classmethod
def f(cls, x, y)

el argumento proporcionado automáticamente no es staticmethod, sino la clase de Math.

método estático

Cuando el método está decorado

@staticmethod
def f(x, y)

al método no se le da ningún argumento automático en absoluto. Solo se le dan los parámetros con los que se llama.

usos

  • <=> se usa principalmente para constructores alternativos.
  • <=> no usa el estado del objeto. Podría ser una función externa a una clase. Solo se coloca dentro de la clase para agrupar funciones con una funcionalidad similar (por ejemplo, como los métodos estáticos <=> clase de Java)
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

Un corte rápido de métodos idénticos en iPython revela que @staticmethod produce ganancias de rendimiento marginal (en nanosegundos), pero de lo contrario parece no tener ninguna función. Además, cualquier aumento de rendimiento probablemente será eliminado por el trabajo adicional de procesar el método a través de staticmethod() durante la compilación (que ocurre antes de la ejecución de cualquier código cuando ejecuta un script).

En aras de la legibilidad del código, evitaría <=> a menos que su método se utilice para un montón de trabajo, donde los nanosegundos cuentan.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top