¿Es posible cambiar la implementación del método de una instancia sin cambiar todas las demás instancias de la misma clase?

No sé mucho de python (nunca lo usé antes: D), pero parece que no puedo encontrar nada en línea. Tal vez simplemente no busqué en google la pregunta correcta, pero aquí voy:

Quiero cambiar la implementación de una instancia de un método específico. Cuando busqué en Google, encontré que podías hacerlo, pero cambia la implementación para todas las demás instancias de la misma clase, por ejemplo:

def showyImp(self): print self.y class Foo: def __init__(self): self.x = "x = 25" self.y = "y = 4" def showx(self): print self.x def showy(self): print "y = woohoo" class Bar: def __init__(self): Foo.showy = showyImp self.foo = Foo() def show(self): self.foo.showx() self.foo.showy() if __name__ == '__main__': b = Bar() b.show() f = Foo() f.showx() f.showy() 

Esto no funciona como se esperaba, porque la salida es la siguiente:

x = 25

y = 4

x = 25

y = 4

Y quiero que sea:

x = 25

y = 4

x = 25

y = woohoo

Intenté cambiar el método de inicio de Bar con esto:

 def __init__(self): self.foo = Foo() self.foo.showy = showyImp 

Pero me sale el siguiente mensaje de error:

showyImp () toma exactamente 1 argumento (0 dado)

Así que sí … intenté usar setattr() , pero parece que es lo mismo que self.foo.showy = showyImp .

¿Cualquier pista? 🙂

Todo lo que querías saber sobre los atributos y métodos de Python .

Sí, esta es una respuesta indirecta, pero muestra varias técnicas y explica algunos de los detalles más intrincados y la “magia”.

Para una respuesta “más directa”, considere el nuevo módulo de python . En particular, observe la función instancemethod que permite “vincular” un método a una instancia; en este caso, le permitiría usar “self” en el método.

 import new class Z(object): pass z = Z() def method(self): return self zq = new.instancemethod(method, z, None) z is zq() # true 

Desde Python 2.6, debe usar la clase MethodType del módulo de MethodType :

 from types import MethodType class A(object): def m(self): print 'aaa' a = A() def new_m(self): print 'bbb' am = MethodType(new_m, a) 

Sin embargo, como señaló otra respuesta, esto no funcionará con los métodos “mágicos” de las clases de nuevo estilo, como __str__() .

Si alguna vez necesitas hacerlo por un método especial (que, para una clase de estilo nuevo, que es lo que siempre deberías usar y el único tipo en Python 3, se busca en la clase, no en la instancia) , puedes hacer una clase por instancia, por ejemplo …

 self.foo = Foo() meths = {'__str__': lambda self: 'peekaboo!'} self.foo.__class__ = type('yFoo', (Foo,), meths) 

Edición : se me ha pedido que aclare las ventajas de este enfoque con el nuevo método de instalación …

 >>> class X(object): ... def __str__(self): return 'baah' ... >>> x=X() >>> y=X() >>> print x, y baah baah >>> x.__str__ = new.instancemethod(lambda self: 'boo!', x) >>> print x, y baah baah 

Como se puede ver, el nuevo método de instalación es totalmente inútil en este caso. OTOH …:

 >>> x.__class__=type('X',(X,),{'__str__':lambda self:'boo!'}) >>> print x, y boo! baah 

… asignar una nueva clase funciona muy bien para este caso y para todos los demás. Por cierto, como espero que quede claro, una vez que haya hecho esto en una instancia determinada, más tarde podrá agregar más métodos y otros atributos de clase a su x.__class__ y afectar intrínsecamente solo esa instancia!

Si está vinculando a la instancia, no debe incluir el argumento auto:

 >>> class Foo(object): ... pass ... >>> def donothing(): ... pass ... >>> f = Foo() >>> fx = donothing >>> fx() >>> 

Sin embargo, necesitas el argumento propio si estás vinculado a una clase:

 >>> def class_donothing(self): ... pass ... >>> foo.y = class_donothing >>> fy() >>> 

Su ejemplo es retorcido y complejo, y no veo qué tiene que ver con su pregunta. Siéntase libre de aclarar si lo desea.

Sin embargo, es bastante fácil hacer lo que estás buscando hacer, asumiendo que estoy leyendo tu pregunta correctamente.

 class Foo(object): def bar(self): print('bar') def baz(): print('baz') 

En un intérprete …

 >>> f = Foo() >>> f.bar() bar >>> f.bar = baz >>> f.bar() baz >>> g = Foo() >>> g.bar() bar >>> f.bar() baz 

No hagas esto.

Cambiar los métodos de una instancia es simplemente incorrecto.

Aquí están las reglas de OO Design.

  1. Evita la magia.

  2. Si no puedes usar la herencia, usa la delegación.

Eso significa que cada vez que crees que necesitas algo mágico, deberías haber estado escribiendo una “envoltura” o Fachada alrededor del objeto para agregar las funciones que deseas.

Sólo escribe un envoltorio.