¿Cuál es la diferencia entre @staticmethod y @classmethod en Python?
¿ Cuál es la diferencia entre un método decorado con @staticmethod
y uno decorado con @classmethod
?
Quizás un poco de código de ejemplo ayude: observe la diferencia en las firmas de llamada de y foo
:class_foo
static_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, a
se 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_foo
usando 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.
foo
es solo una función, pero cuando llamas a.foo
no solo obtienes la función, sino que obtienes una versión "parcialmente aplicada" de la función con la instancia del objeto a
vinculada como primer argumento de la función. foo
espera 2 argumentos, mientras que a.foo
solo 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 hacerlo class_foo
, sino que la clase A
está 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_foo
simplemente devuelve una buena función sin argumentos vinculados. static_foo
espera 1 argumento y
a.static_foo
también espera 1 argumento.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
Y, por supuesto, sucede lo mismo cuando llamas static_foo
a la clase A
.
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
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
>>>
Básicamente @classmethod
crea un método cuyo primer argumento es la clase desde la que se llama (en lugar de la instancia de la clase), @staticmethod
no tiene ningún argumento implícito.
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