After monkey patching an instance method (using types.MethodType
) I pass the monkey patched object to a library which copies it (using copy.copy
) and calls the monkey patched method. The monkey patched method gets called successfully but the self
parameter references to the old (not copied object).
import copy
import types
class Person:
def __init__(self, lang):
self.lang = lang
def hello(self, n):
print(id(self), f"{n} times hello in {self.lang}")
def mean_hello(self, n):
print(id(self), f"{n} times mean hello in {self.lang}")
a = Person('English')
b = copy.copy(a)
b.lang = "French"
print(id(a), id(b)) # 139885310130440 139885310130720
a.hello(1) # 139885310130440 1 times hello in English
b.hello(1) # 139885310130720 1 times hello in French
a.hello = types.MethodType(mean_hello, a)
c = copy.copy(a)
c.lang = 'German'
print(id(a), id(c)) # 139885310130440 139885310130664
a.hello(2) # 139885310130440 2 times mean hello in English
c.hello(2) # 139885310130440 2 times mean hello in English
From the example it can be seen that the last line c.hello(2)
calls the monkey patched method with self
referencing to a
. Why does this happen? Is there a way to avoid this?
As pointed out by @MisterMiyagi, the method created through types.MethodType
is bound to the object a
(it can only be called on a
) and assigning it to object c
(by copying) does not make it unbound.
The solution outlined by @mananony is to subclass Person
and override the method in the subclass. This way the method will remain unbound, linked to the subclass and can be called with any object of type MeanInner
.
I put the subclass in a decorator because in my real code Person
actually represents a lot of classes with hello
method and I want to be able to change all of them.
def mean(person_cls):
class MeanInner(person_cls):
def hello(self, n):
print(id(self), f"{n} times mean hello in {self.lang}")
return MeanInner
a = mean(Person)('English')
c = copy.copy(a)
c.lang = 'German'
print(id(a), id(c)) # 140407794133424 140407794133536
a.hello(2) # 140407794133424 2 times mean hello in English
c.hello(2) # 140407794133536 2 times mean hello in German