How can I add behavior to an instance of a class without changing the base class

I get a class instance from a third-party library in my code

i = x.get_instance()

Then my code calls the method from this instance.

i.method_a() 

which will call the method inside this class where I want to add behavior.

The only way I found now is

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 best way to add this behavior, since I cannot change the instance that I will return. I do not initialize it myself.

+4
source share
3 answers

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 typeis the class. funcTypeis an example of a method:

>>> funcType
<type 'instancemethod'>
>>> help(funcType)
...
class instancemethod(object)
 |  instancemethod(function, instance, class)
 |
 |  Create an instance method object.

...

Also (thanks to @bruno desthuilliers) you could just do:

a.foo = new_foo.__get__(a, type(a))

instead of funcType.

+1
source

, x.get_instance() ThirdPartyClass, 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()

, - , , :

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()

, x.get_instance(), .

- , :

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())

wrt/make sure, , , monkeypatch.

: patch_instance , x.get_instance() , x.get_instance, patch_instance.

0

Suppose you have a class Foo that 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 fix 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 fix the instance without monkey-fixing the class:

>>> import types

>>> foo.bar = types.MethodType(bar, foo, Foo)

>>> foo.bar()
bar eggs
spam eggs

Not sure if this is correct (maybe this is not a good idea), but it works.

Source class intact:

>>> otherfoo = Foo('foo')

>>> otherfoo.bar()
bar foo
0
source

Source: https://habr.com/ru/post/1570175/


All Articles