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

Resuelto Daryl Spitzer asked hace 15 años • 36 respuestas

¿ Cuál es la diferencia entre un método decorado con @staticmethody uno decorado con @classmethod?

Daryl Spitzer avatar Sep 26 '08 04:09 Daryl Spitzer
Aceptado

Quizás un poco de código de ejemplo ayude: observe la diferencia en las firmas de llamada de y foo:class_foostatic_foo

class A(object):
    def foo(self, x):
        print(f"executing foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"executing class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"executing static_foo({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, ase pasa implícitamente como 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 primer argumento en lugar de self.

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

También puedes llamar class_foousando la clase. De hecho, si define algo como un método de clase, probablemente sea porque pretende llamarlo desde la clase en lugar de desde 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 pasan 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.


fooes solo una función, pero cuando llamas a.foono solo obtienes la función, sino que obtienes una versión "parcialmente aplicada" de la función con la instancia del objeto avinculada como primer argumento de la función. fooespera 2 argumentos, mientras que a.foosolo espera 1 argumento.

aestá 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, ano está obligado a hacerlo class_foo, sino que la clase Aestá obligada a hacerlo 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_foosimplemente devuelve una buena función sin argumentos vinculados. static_fooespera 1 argumento y a.static_footambién espera 1 argumento.

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

Y, por supuesto, sucede lo mismo cuando llamas static_fooa la clase A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>
unutbu avatar Nov 03 '2009 19:11 unutbu

Un método estático es un método que no sabe nada acerca de la clase o instancia a la que fue llamado. Simplemente obtiene los argumentos que se aprobaron, ningún primer argumento implícito.

Un método de clase , por otro lado, es un método al que se le pasa la clase en la que fue llamado, o la clase de la instancia en la que fue llamado, 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 fue llamada como primer argumento, siempre puede crear una instancia de la clase correcta, incluso cuando hay subclases involucradas. Observe, por ejemplo, cómo dict.fromkeys(), un método de clase, 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
>>> 
Thomas Wouters avatar Sep 25 '2008 21:09 Thomas Wouters

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

Terence Simpson avatar Sep 25 '2008 21:09 Terence Simpson

Para decidir si usar @staticmethod o @classmethod debes 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 another class,
        #       you don't have to rename the referenced class 

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

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing apple %d...' % apple)
        cls._counter += 1
Du D. avatar Apr 22 '2016 15:04 Du D.