¿Cuál es la diferencia entre una función decorada @staticmethod
y una decorada con @classmethod
?
python
oop
methods
python-decorators
Daryl Spitzer
fuente
fuente
Respuestas:
Tal vez un poco de ejemplo de código ayudará a: Aviso de la diferencia de las firmas de llamadas
foo
,class_foo
ystatic_foo
: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.Con classmethods , la clase de la instancia del objeto se pasa implícitamente como el primer argumento en lugar de
self
.También puedes llamar
class_foo
usando la clase. De hecho, si define algo como un método de clase, probablemente sea porque tiene la intención de llamarlo desde la clase en lugar de desde una instancia de clase.A.foo(1)
habría generado un TypeError, peroA.class_foo(1)
funciona bien: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) nicls
(la clase) se pasa implícitamente como primer argumento. Se comportan como funciones simples, excepto que puede llamarlas desde una instancia o la clase:Los métodos estáticos se utilizan para agrupar funciones que tienen alguna conexión lógica con una clase a la clase.
foo
es solo una función, pero cuando llamaa.foo
no solo obtiene la función, obtiene una versión "parcialmente aplicada" de la función con la instancia del objetoa
vinculada como el primer argumento de la función.foo
espera 2 argumentos, mientras quea.foo
solo espera 1 argumento.a
está obligado afoo
. Eso es lo que se entiende por el término "obligado" a continuación:Con
a.class_foo
,a
no está obligado aclass_foo
, más bienA
está obligado a la claseclass_foo
.Aquí, con un método estático, aunque es un método,
a.static_foo
simplemente devuelve una buena función 'ole sin argumentos vinculados.static_foo
espera 1 argumento, ya.static_foo
espera 1 argumento también.Y, por supuesto, sucede lo mismo cuando llamas
static_foo
con la claseA
.fuente
@staticmethod
podría ayudar a organizar su código al ser reemplazable por subclases. Sin él, tendrías variantes de la función flotando en el espacio de nombres del módulo.@staticmethod
: puede usarlo para eliminar cruft. Estoy implementando un lenguaje de programación en Python: las funciones definidas por la biblioteca utilizan unexecute
método estático , donde las funciones definidas por el usuario requieren argumentos de instancia (es decir, el cuerpo de la función). Este decorador elimina las advertencias de "parámetro propio no utilizado" en el inspector de PyCharm.Un método estático es un método que no sabe nada acerca de la clase o instancia en la que fue invocado. 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 método de clase , 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
dict.fromkeys()
, cómo , un método de clase, devuelve una instancia de la subclase cuando se llama en una subclase:fuente
Básicamente,
@classmethod
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.fuente
Documentos oficiales de Python:
@classmethod
@staticmethod
fuente
Aquí hay un breve artículo sobre esta pregunta.
fuente
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, entonces use @classmethod . Por otro lado, si su método no toca ninguna otra parte de la clase, use @staticmethod.
fuente
cls._counter
aún lo seríacls._counter
incluso si el código se coloca en una clase diferente o si se cambia el nombre de la clase.Apple._counter
es específico para laApple
clase; para una clase diferente, o cuando se cambia el nombre de la clase, necesitaría cambiar la clase referenciada.Es posible que haya visto código 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:
El método de instancia normal
Primero te lo explicaré
a_normal_instance_method
. Esto se llama precisamente un " método de instancia ". 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 de 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 usamos 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']
:Métodos vinculados
Los métodos de instancia se pueden vincular mediante una búsqueda punteada para su uso posterior.
Por ejemplo, esto vincula el
str.join
método a la':'
instancia: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:
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 usa.
Sin embargo, si está adjunto al objeto, seguirá el objeto convenientemente a través de la importación y la herencia también.
Un ejemplo de un método estático es
str.maketrans
, movido desde elstring
módulo en Python 3. Hace que una tabla de traducción sea adecuada para el consumo destr.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 elstring
módulo es bastante torpe, y es bueno poder llamarlo desde la clase, como enstr.maketrans
En python 2, debe importar esta función desde el módulo de cadena cada vez menos útil:
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
dict.fromkeys
. 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).Cuando subclasemos dict, podemos usar el mismo constructor, que crea una instancia de la subclase.
Vea el código fuente de los pandas para otros ejemplos similares de constructores alternativos, y también vea la documentación oficial de Python en
classmethod
ystaticmethod
.fuente
Comencé a aprender 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 del constructor. Y para lograr esto, podrías usar
classmethod
. El siguiente ejemplo explicará estoVamos a considerar que tenemos una
Person
clase que toma dos argumentosfirst_name
ylast_name
y crea la instancia dePerson
.Ahora, si el requisito viene donde necesita crear una clase usando un solo nombre, solo un
first_name
, no puede hacer algo así en Python.Esto le dará un error cuando intente crear un objeto (instancia).
Sin embargo, podría lograr lo mismo usando
@classmethod
como se menciona a continuaciónMétodo estático: esto es bastante simple, no está vinculado a la instancia o clase y simplemente puede llamarlo usando el nombre de la clase.
Entonces, digamos en el ejemplo anterior que necesita una validación que
first_name
no debe exceder los 20 caracteres, simplemente puede hacer esto.y simplemente puedes llamar usando
class name
fuente
def __init__(self, first_name, last_name="")
lugar del método de claseget_person
. También el resultado será exactamente el mismo en este caso.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 excelente manera de hacer singletons o clases de fábrica que controlan la cantidad 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.
fuente
@decorators se agregaron en python 2.4 Si está usando python <2.4, puede usar las funciones 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:
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 el clúster de clase internamente. El método de clase solo necesita información sobre la clase y ninguna instancia del objeto.
Otro beneficio de hacer que el
_is_cluster_for
método 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 simplemente verificar el nombre de la clase no sería suficiente.fuente
Métodos Estáticos:
Beneficios de los métodos estáticos:
Más conveniente para importar en comparación con funciones de nivel de módulo ya que cada método no tiene que importarse especialmente
Métodos de clase:
Estos se crean con la función incorporada classmethod.
fuente
@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: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:fuente
c = C(); c.foo()
plantea AttributeError, tendrías que hacertype(c).foo()
. Esto también podría considerarse una característica, aunque no puedo pensar por qué querrías hacerlo.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 resumirlo de la siguiente manera.
@staticmethod
La función no es más que una función definida dentro de una clase. Es invocable sin instanciar la clase primero. Su definición es inmutable a través de la herencia.@classmethod
La función también se puede llamar sin crear instancias de la clase, pero su definición sigue a la subclase, no a la clase padre, por herencia, puede ser anulada por subclase. Esto se debe a que el primer argumento para la@classmethod
función siempre debe ser cls (clase).fuente
Solo el primer argumento difiere :
Con más detalle...
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étododebe llamarse con 2 argumentos.
self
se pasa automáticamente y es el objeto mismo .método de clase
Cuando el método está decorado
el argumento proporcionado automáticamente no es
self
, sino la clase deself
.método estático
Cuando el método está decorado
el método no recibe ningún argumento automático en absoluto. Solo se le dan los parámetros con los que se llama.
usos
classmethod
se usa principalmente para constructores alternativos.staticmethod
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 losMath
métodos estáticos de clase de Java )fuente
Permítanme decir la similitud entre un método decorado con @classmethod vs @staticmethod primero.
Similitud: Ambos se pueden invocar en la Clase en sí, en lugar de solo la instancia de la clase. Entonces, ambos en cierto sentido son los métodos de Class .
Diferencia: un método de clase recibirá la clase en sí como primer argumento, mientras que un método estático no.
Por lo tanto, un método estático, en cierto sentido, no está vinculado a la Clase en sí misma y simplemente está colgando allí solo porque puede tener una funcionalidad relacionada.
fuente
Otra consideración con respecto al método estático vs método de clase surge con la herencia. Digamos que tienes la siguiente clase:
Y luego desea anular
bar()
en una clase secundaria:Esto funciona, pero tenga en cuenta que ahora la
bar()
implementación en la clase secundaria (Foo2
) ya no puede aprovechar nada específico de esa clase. Por ejemplo, supongamos queFoo2
tenía un método llamadomagic()
que desea utilizar en laFoo2
implementación debar()
:La solución en este caso sería llamar
Foo2.magic()
enbar()
, pero luego te estás repitiendo (si el nombre deFoo2
los cambios, tendrá que acordarse de actualizar esebar()
método).Para mí, esto es una ligera violación del principio abierto / cerrado , ya que una decisión tomada
Foo
está afectando su capacidad de refactorizar el código común en una clase derivada (es decir, es menos abierto a la extensión). Sibar()
fuera unclassmethod
estaríamos bien:Da:
In Foo2 MAGIC
fuente
Trataré de explicar la diferencia básica usando un ejemplo.
1 - podemos llamar directamente a métodos estáticos y de clase sin inicializar
2- El método estático no puede llamar al método propio, 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.
4- El método de clase no está vinculado a un objeto sino a una clase.
fuente
@classmethod: se puede usar para crear un acceso global compartido a todas las instancias creadas de esa clase ... como actualizar un registro por varios usuarios ... Particularmente, también encontré que es útil al crear 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
fuente
Es posible que desee considerar la diferencia entre:
y
Esto ha cambiado entre python2 y python3:
python2:
python3:
Por lo tanto, el uso
@staticmethod
de 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@staticmethod
decorador.Los otros casos han sido bien cubiertos por la respuesta de unutbus.
fuente
Un método de clase recibe la clase como primer argumento implícito, al igual que un método de instancia recibe la instancia. Es un método que está vinculado a la clase y no al objeto de la clase. Tiene acceso al estado de la clase ya que toma un parámetro de clase que apunta a la clase y no a la instancia del objeto. Puede modificar un estado de clase que se aplicaría en todas las instancias de la clase. Por ejemplo, puede modificar una variable de clase que será aplicable a todas las instancias.
Por otro lado, un método estático no recibe un primer argumento implícito, en comparación con los métodos de clase o los métodos de instancia. Y no puede acceder ni modificar el estado de la clase. Solo pertenece a la clase porque desde el punto de vista del diseño esa es la forma correcta. Pero en términos de funcionalidad no está vinculado, en tiempo de ejecución, a la clase.
como guía, use métodos estáticos como utilidades, use métodos de clase, por ejemplo, como fábrica. O tal vez para definir un singleton. Y use métodos de instancia para modelar el estado y el comportamiento de las instancias.
Espero haber sido claro!
fuente
Mi contribución demuestra la diferencia entre
@classmethod
,@staticmethod
y los métodos de instancia, incluida la forma en que una instancia puede llamar indirectamente a@staticmethod
. Pero en lugar de llamar indirectamente a@staticmethod
desde una instancia, hacerlo privado puede ser más "pitónico". Obtener algo de un método privado no se demuestra aquí, pero es básicamente el mismo concepto.fuente
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.
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.
fuente
Analice @staticmethod literalmente proporcionando diferentes ideas.
Un método normal de una clase es un método dinámico implícito 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 llama '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.
fuente
Creo que dar una versión puramente de Python
staticmethod
yclassmethod
ayudaría a comprender la diferencia entre ellos a nivel de lenguaje.Ambos son descriptores que no son de datos (sería más fácil entenderlos si está familiarizado con los descriptores primero).
fuente
El método estático no tiene acceso a atributos del objeto, de la clase o de las clases primarias en la jerarquía de herencia. Se puede invocar directamente en la clase (sin crear un objeto).
classmethod no tiene acceso a los atributos del objeto. Sin embargo, puede acceder a los atributos de la clase y de las clases primarias en la jerarquía de herencia. Se puede invocar directamente en la clase (sin crear un objeto). Si se llama al objeto, es el mismo método normal que no accede
self.<attribute(s)>
yself.__class__.<attribute(s)>
solo accede .Creemos que tenemos una clase
b=2
, crearemos un objeto y lo restableceremosb=4
en él. El método estático no puede acceder a nada de lo anterior. Classmethod.b==2
solo puede acceder a través decls.b
. El método normal puede acceder a ambos:.b==4
víaself.b
y.b==2
víaself.__class__.b
.Podríamos seguir el estilo KISS (manténgalo simple, estúpido): no use métodos estáticos y métodos de clase, no use clases sin instanciarlos, acceda solo a los atributos del objeto
self.attribute(s)
. Hay idiomas en los que la OOP se implementa de esa manera y creo que no es mala idea. :)fuente
Un rápido corte 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étodostaticmethod()
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
@staticmethod
menos que su método se utilice para un montón de trabajo, donde los nanosegundos cuentan.fuente