¿Cuál es la diferencia entre los siguientes métodos de clase?
¿Es que uno es estático y el otro no?
class Test(object):
def method_one(self):
print "Called method_one"
def method_two():
print "Called method_two"
a_test = Test()
a_test.method_one()
a_test.method_two()
@classmethod
a la definición. Se debe llamar al primer parámetro encls
lugar deself
y recibirá el objeto de clase en lugar de una instancia de su clase:Test.method_three()
ya_test.method_three()
son equivalentes.self
argumento? ¿Hay un fuerte caso de uso para esto?Respuestas:
En Python, hay una distinción entre métodos vinculados y no vinculados .
Básicamente, una llamada a una función miembro (como
method_one
), una función enlazadase traduce a
es decir, una llamada a un método independiente. Debido a eso, una llamada a su versión de
method_two
fallará con unTypeError
Puedes cambiar el comportamiento de un método usando un decorador
El decorador le dice a la metaclase predeterminada incorporada
type
(la clase de una clase, cf. esta pregunta ) que no cree métodos vinculadosmethod_two
.Ahora, puede invocar el método estático tanto en una instancia como en la clase directamente:
fuente
Los métodos en Python son una cosa muy, muy simple una vez que comprende los conceptos básicos del sistema descriptor. Imagina la siguiente clase:
Ahora echemos un vistazo a esa clase en el shell:
Como puede ver si accede al
foo
atributo en la clase, obtiene un método independiente, sin embargo, dentro del almacenamiento de la clase (el dict) hay una función. ¿Porque eso? La razón de esto es que la clase de su clase implementa un__getattribute__
que resuelve descriptores. Suena complejo, pero no lo es.C.foo
es más o menos equivalente a este código en ese caso especial:Esto se debe a que las funciones tienen un
__get__
método que las convierte en descriptores. Si tiene una instancia de una clase, es casi la misma, solo queNone
es la instancia de la clase:Ahora, ¿por qué Python hace eso? Porque el objeto del método une el primer parámetro de una función a la instancia de la clase. De ahí viene el yo. Ahora a veces no quieres que tu clase convierta una función en un método, ahí es donde
staticmethod
entra en juego:El
staticmethod
decorador envuelve su clase e implementa un ficticio__get__
que devuelve la función envuelta como función y no como método:Espero que eso lo explique.
fuente
staticmethod
decorador ajusta su clase (...) Esta frase es un poco engañosa ya que la clase que se está envolviendo es la clase del métodofoo
y no la clase en la quefoo
se define.Cuando llama a un miembro de la clase, Python usa automáticamente una referencia al objeto como primer parámetro. La variable en
self
realidad no significa nada, es solo una convención de codificación. Podrías llamarlogargaloo
si quisieras. Dicho esto, la llamada amethod_two
elevaría aTypeError
, porque Python intenta automáticamente pasar un parámetro (la referencia a su objeto padre) a un método que se definió como que no tiene parámetros.Para que realmente funcione, puede agregar esto a su definición de clase:
o puedes usar la
@staticmethod
función decorador .fuente
fuente
Class.instance_method() # The class cannot use an instance method
puede usar Simplemente pase la instancia manualmente:Class.instance_method(a)
TyeError
línea.a.class_method()
, parece quea.c
se actualizó a1
, así que la llamadaClass.class_method()
, actualizó laClass.c
variable a2
. Sin embargo, cuando lo asignóa.c=5
, ¿por quéClass.c
no se actualizó5
?method_two no funcionará porque estás definiendo una función miembro pero no le estás diciendo de qué es miembro la función. Si ejecuta la última línea obtendrá:
Si está definiendo funciones miembro para una clase, el primer argumento siempre debe ser 'self'.
fuente
Explicación precisa de Armin Ronacher arriba, ampliando sus respuestas para que los principiantes como yo lo entiendan bien:
La diferencia en los métodos definidos en una clase, ya sea método estático o de instancia (hay otro tipo - método de clase - no discutido aquí, así que omitiéndolo), radica en el hecho de si de alguna manera están vinculados a la instancia de clase o no. Por ejemplo, diga si el método recibe una referencia a la instancia de clase durante el tiempo de ejecución
La
__dict__
propiedad de diccionario del objeto de clase contiene la referencia a todas las propiedades y métodos de un objeto de clase y, por lo tanto,El método foo es accesible como el anterior. Un punto importante a tener en cuenta aquí es que todo en Python es un objeto y, por lo tanto, las referencias en el diccionario anterior apuntan a otros objetos. Permítanme llamarlos objetos de propiedad de clase, o como CPO dentro del alcance de mi respuesta por brevedad.
Si un CPO es un descriptor, el intérprete de Python llama al
__get__()
método del CPO para acceder al valor que contiene.Para determinar si un CPO es un descriptor, el intérprete de Python verifica si implementa el protocolo del descriptor. Implementar el protocolo descriptor es implementar 3 métodos
por ej.
dónde
self
es el CPO (podría ser una instancia de list, str, function, etc.) y lo proporciona el tiempo de ejecucióninstance
es la instancia de la clase en la que se define este CPO (el objeto 'c' anterior) y debe ser provisto explícitamente por nosotrosowner
es la clase donde se define este CPO (el objeto de clase 'C' anterior) y necesita que lo suministremos nosotros. Sin embargo, esto se debe a que lo estamos llamando al CPO. cuando lo llamamos en la instancia, no necesitamos suministrar esto ya que el tiempo de ejecución puede suministrar la instancia o su clase (polimorfismo)value
es el valor previsto para el CPO y debe ser suministrado por nosotrosNo todos los CPO son descriptores. Por ejemplo
Esto se debe a que la clase de lista no implementa el protocolo descriptor.
Por lo tanto, el argumento self in
c.foo(self)
se requiere porque su firma de método es realmente estoC.__dict__['foo'].__get__(c, C)
(como se explicó anteriormente, C no es necesario, ya que se puede encontrar o polimorfizar) Y esta es también la razón por la que obtiene un TypeError si no pasa ese argumento de instancia requerido.Si observa que todavía se hace referencia al método a través de la clase Objeto C y el enlace con la instancia de clase se logra al pasar un contexto en la forma del objeto de instancia a esta función.
Esto es bastante sorprendente, ya que si opta por no mantener ningún contexto o ningún enlace a la instancia, todo lo que se necesitaba era escribir una clase para ajustar el CPO del descriptor y anular su
__get__()
método para no requerir contexto. Esta nueva clase es lo que llamamos un decorador y se aplica a través de la palabra clave@staticmethod
La ausencia de contexto en el nuevo CPO envuelto
foo
no arroja un error y se puede verificar de la siguiente manera:El caso de uso de un método estático es más un espacio de nombres y de mantenimiento de código (sacarlo de una clase y ponerlo a disposición en todo el módulo, etc.).
Tal vez sea mejor escribir métodos estáticos en lugar de métodos de instancia siempre que sea posible, a menos que, por supuesto, necesite contexualizar los métodos (como variables de instancia de acceso, variables de clase, etc.). Una razón es facilitar la recolección de basura al no mantener referencias no deseadas a los objetos.
fuente
Eso es un error.
en primer lugar, la primera línea debería ser así (tenga cuidado con las mayúsculas)
Cada vez que llama a un método de una clase, se convierte en el primer argumento (de ahí el nombre self) y method_two da este error
fuente
El segundo no funcionará porque cuando lo llamas así, python intenta internamente llamarlo con la instancia a_test como primer argumento, pero tu method_two no acepta ningún argumento, por lo que no funcionará, obtendrás un tiempo de ejecución error. Si desea el equivalente de un método estático, puede usar un método de clase. Hay mucha menos necesidad de métodos de clase en Python que los métodos estáticos en lenguajes como Java o C #. La mayoría de las veces, la mejor solución es usar un método en el módulo, fuera de una definición de clase, que funcionan de manera más eficiente que los métodos de clase.
fuente
Class Test(object): @staticmethod def method_two(): print(“called method_two”)
Un caso de uso, en el que estaba pensando es cuando desea que la función sea parte de una clase, pero no desea que el usuario acceda a la función directamente. Por lo que lamethod_two
puede llamar otras funciones dentro de laTest
instancia, pero no puedo ser llamado usandoa_test.method_two()
. Si lo usodef method_two()
, ¿funcionará para este caso de uso? ¿O hay una mejor manera de modificar la definición de la función, para que funcione según lo previsto para el caso de uso anterior?La llamada a method_two arrojará una excepción por no aceptar el parámetro propio, el tiempo de ejecución de Python lo pasará automáticamente.
Si desea crear un método estático en una clase de Python, decórelo con el
staticmethod decorator
.fuente
Lea estos documentos de Guido First Class. Todo explica claramente cómo nacen los métodos independientes y vinculados.
fuente
La definición de
method_two
no es válida. Cuando llamemethod_two
, recibiráTypeError: method_two() takes 0 positional arguments but 1 was given
del intérprete.Un método de instancia es una función acotada cuando lo llamas como
a_test.method_two()
. Acepta automáticamenteself
, que apunta a una instancia deTest
, como su primer parámetro. A través delself
parámetro, un método de instancia puede acceder libremente a los atributos y modificarlos en el mismo objeto.fuente
Métodos independientes
Los métodos no vinculados son métodos que aún no están vinculados a ninguna instancia de clase en particular.
Métodos vinculados
Los métodos vinculados son los que están vinculados a una instancia específica de una clase.
Como se documenta aquí , self puede referirse a diferentes cosas dependiendo de que la función esté vinculada, no vinculada o estática.
Eche un vistazo al siguiente ejemplo:
Como no utilizamos la instancia de clase
obj
- en la última llamada, podemos decir que parece un método estático.Si es así, ¿cuál es la diferencia entre una
MyClass.some_method(10)
llamada y una llamada a una función estática decorada con un@staticmethod
decorador?Al usar el decorador, dejamos explícitamente claro que el método se usará sin crear primero una instancia para él. Normalmente uno no esperaría que los métodos de los miembros de la clase se usen sin la instancia y acceder a ellos puede causar posibles errores dependiendo de la estructura del método.
Además, al agregar el
@staticmethod
decorador, también estamos haciendo posible que se pueda llegar a través de un objeto.No puede hacer el ejemplo anterior con los métodos de instancia. Puede sobrevivir al primero (como lo hicimos antes), pero el segundo se traducirá en una llamada no vinculada
MyClass.some_method(obj, 10)
que generará una,TypeError
ya que el método de instancia toma un argumento y usted intentó pasar dos sin intención.Entonces, podría decir: "si puedo llamar a métodos estáticos a través de una instancia y una clase,
MyClass.some_static_method
yMyClass().some_static_method
deberían ser los mismos métodos". ¡Si!fuente
Método enlazado = método de instancia
Método independiente = método estático.
fuente