Para explicar excelente respuesta de @ codelogic, propongo un enfoque más explícito.Esta es la misma técnica que el operador .
realiza para vincular un método de clase cuando accede a él como un atributo de instancia, excepto que su método será en realidad una función definida fuera de una clase.
Al trabajar con el código @codelogic, la única diferencia está en cómo se enlaza el método. Estoy usando el hecho de que las funciones y los métodos no son datos descriptors en Python e invocan el método __get__
. Tenga en cuenta especialmente que tanto el original como el reemplazo tienen firmas idénticas, lo que significa que puede escribir el reemplazo como un método de clase completa, accediendo a todos los atributos de instancia a través del self
.
class Dog:
def bark(self):
print "Woof"
def new_bark(self):
print "Woof Woof"
foo = Dog()
# "Woof"
foo.bark()
# replace bark with new_bark for this object only
foo.bark = new_bark.__get__(foo, Dog)
foo.bark()
# "Woof Woof"
Al asignar un método vinculado a un atributo de instancia, que han creado una simulación casi completa de sustituir un método. Una característica útil que falta es el acceso a la versión no-arg de super
, ya que no se encuentra en una definición de clase. Otra cosa es que el atributo __name__
de su método encuadernado no tomará el nombre de la función que está anulando, como lo haría en la definición de clase, pero aún puede establecerlo manualmente. La tercera diferencia es que su método de enlace manual es una referencia de atributo simple que simplemente resulta ser una función. El operador .
no hace más que buscar esa referencia. Por otro lado, cuando se invoca un método regular de una instancia, el proceso de enlace crea un nuevo método vinculado en todo momento.
La única razón por la que esto funciona, por cierto, es que los atributos de instancia anulan los descriptores sin datos. Los descriptores de datos tienen métodos __set__
, que los métodos (afortunadamente para ti) no tienen. Los descriptores de datos en la clase realmente tienen prioridad sobre cualquier atributo de instancia. Es por eso que puede asignar una propiedad: se invoca su método __set__
cuando intenta realizar una tarea. Personalmente, me gusta llevar esto un paso más allá y ocultar el valor real del atributo subyacente en el caso __dict__
, donde es inaccesible por medios normales exactamente porque la propiedad lo sombrea.
También debe tener en cuenta que esto no tiene sentido para magic (double underscore) methods. Por supuesto, los métodos mágicos pueden anularse de esta manera, pero las operaciones que los usan solo miran el tipo. Por ejemplo, puede configurar __contains__
como algo especial en su instancia, pero al llamar al x in instance
no tendrá en cuenta eso y usará type(instance).__contains__(instance, x)
. Esto se aplica a todos los métodos mágicos especificados en Python data model.
@arivero: Pensé que el "Por favor, no hagas esto como se muestra" lo dejó perfectamente claro. ¿Qué otra o diferentes palabras le gustaría ver para dejar más claro que esto no está respondiendo la pregunta que se le hizo, sino que le está dando consejos sobre por qué es una mala idea? –
No estoy en desacuerdo en el asesoramiento, ni en el OP como parece. Pero supongo que la gente tiene razones para preguntar. O incluso si el OP no lo ha hecho, otros futuros visitantes podrían hacerlo. Entonces, en mi humilde opinión, una respuesta más una reprimenda es mejor que solo una reprimenda. – arivero
@arivero: Eso no respondió mi pregunta. –