如何在不更改基类的情况下向类的实例添加行为

时间:2015-01-06 15:24:54

标签: python

我在代码

中从第三方库中获取了一个类实例
i = x.get_instance()

然后我的代码从此实例调用一个方法。

i.method_a() 

将在此类中调用我想要添加行为的方法。

我现在找到的唯一方法是

class BetterClass(ThirdPartyClass):
    def getMessage(self):
        message = super(BetterClass, self).getMessage()
        ... add behaviour
        return message    

i.__class__ = BetterClass
i.method_a()

但是添加这种行为的更好方法是什么,因为我无法改变我回来的实例。 我不是自己做的

3 个答案:

答案 0 :(得分:1)

你可以用:

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

此处typeclassfuncType是一个实例方法:

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

...

另外,(感谢@bruno desthuilliers),您可以这样做:

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

而不是使用funcType

答案 1 :(得分:0)

如果您确定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()进行任何调用之前执行此代码。

如果由于任何原因您无法使用上述解决方案,您仍然可以基于每个实例对该方法进行monkeypatch:

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 /确保您只应用此修补程序一次,因此您可能希望添加类似的标记内容并在类monkeypatch版本中进行测试。

作为最后一点:如果您必须回退到patch_instance解决方案,并希望确保所有调用x.get_instance(),则返回已修补的实例,您可能还需要修补x.get_instance以便调用patch_instance

答案 2 :(得分:0)

假设你有一个类Foo,它有一个bar方法:

>>> class Foo(object):
...    def __init__(self, name):
...        self.name = name    
...    def bar(self):
...        print "bar", self.name

如果您创建此类的实例名为foo ...

>>> foo = Foo('eggs')

>>> foo.bar()
bar eggs    

...然后你要修补bar方法。首先定义一个函数:

>>> def bar(self):
...    self.__class__.bar(self)   # you can call the original method
...    print "spam", self.name

您可以在不修补类的情况下修补实例:

>>> import types

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

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

不确定这是否正确(可能不是一个好主意),但它确实有效。

原始课程完好无损:

>>> otherfoo = Foo('foo')

>>> otherfoo.bar()
bar foo