Reputation: 374
I get an instance of class from a third party library in my code
i = x.get_instance()
My code then calls a method from this instance.
i.method_a()
which will call a method inside this class where i want to add behaviour.
The only way i found now was to
class BetterClass(ThirdPartyClass):
def getMessage(self):
message = super(BetterClass, self).getMessage()
... add behaviour
return message
i.__class__ = BetterClass
i.method_a()
But what is the better way to add this kind of behaviour as i can't change the instance i get back. I do not init it myself
Upvotes: 4
Views: 580
Reputation: 77251
Suppose you have a class Foo which has a bar method:
>>> class Foo(object):
... def __init__(self, name):
... self.name = name
... def bar(self):
... print "bar", self.name
If you create an instance of this class called foo
...
>>> foo = Foo('eggs')
>>> foo.bar()
bar eggs
...then you want to patch the bar method. First define a function:
>>> def bar(self):
... self.__class__.bar(self) # you can call the original method
... print "spam", self.name
You can patch the instance without monkey-patching the class:
>>> import types
>>> foo.bar = types.MethodType(bar, foo, Foo)
>>> foo.bar()
bar eggs
spam eggs
Not sure if this is right (probably not a good idea) but it works.
The original class is intact:
>>> otherfoo = Foo('foo')
>>> otherfoo.bar()
bar foo
Upvotes: 0
Reputation: 16556
You can do it with:
>>> class Example(object):
... def foo(self):
... print "foo"
...
>>> a=Example()
>>> a.foo()
foo
>>>
>>> def new_foo(self):
... Example.foo(self)
... print "new"
...
>>> funcType = type(Example.foo)
>>> a.foo = funcType(new_foo, a, Example)
>>> a.foo()
foo
new
Here, type
is the class. funcType
is then an instancemethod:
>>> funcType
<type 'instancemethod'>
>>> help(funcType)
...
class instancemethod(object)
| instancemethod(function, instance, class)
|
| Create an instance method object.
Also, (thanks @bruno desthuilliers), you could just do:
a.foo = new_foo.__get__(a, type(a))
instead of using funcType
.
Upvotes: 1
Reputation: 77892
If you know for sure that x.get_instance()
will return an instance of ThirdPartyClass
, you can monkeypatch ThirdPartyClass
:
from thirdpartlib.module import ThirdPartyClass
def patch_ThirdPartyClass():
_get_message = ThirdPartyClass.get_message
def get_message(self):
message = _get_message()
# add behaviour here
return message
ThirdPartyClass.get_message = get_message
patch_ThirdPartyClass()
You just want to make sure this code will be executed only once per process - if you're not sure you can garantee this, you'd better add some flag:
def patch_ThirdPartyClass():
_get_message = ThirdPartyClass.get_message
if getattr(_get_message, "patched", False):
return
def get_message(self):
message = _get_message()
# add behaviour here
return message
get_message.patched = True
ThirdPartyClass.get_message = get_message
patch_ThirdPartyClass()
Also you want to make sure this code is executed before any call to x.get_instance()
, obviously.
If for any reason you cannot use the above solution, you can still monkeypatch the method on a per-instance basis:
def patch_instance(instance):
_get_message = instance.get_message
def get_message(self):
message = _get_message()
# add behaviour here
return message
instance.get_message = get_message.__get__(instance, type(instance))
return instance
i = patch_instance(x.get_instance())
Same consideration applies wrt/ making sure you only apply this patch once, so you may want to add a similar flag stuff and test as in the class monkeypatch version.
As a last note: if you have to fallback to the patch_instance
solution and want to make sure all calls to x.get_instance()
return a patched instance, you may also want to patch x.get_instance
so it does the call to patch_instance
.
Upvotes: 0