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

How can i add behaviour to a instance of a class without changing the base class

我从我的代码中的第三方库中获取了 class 的实例

i = x.get_instance()

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

i.method_a() 

这将在我想添加行为的 class 中调用一个方法。

我现在找到的唯一方法是

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

i.__class__ = BetterClass
i.method_a()

但是添加这种行为的更好方法是什么,因为我无法更改返回的实例。 我自己没有初始化

您可以使用:

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

这里,type就是classfuncType 是一个实例方法:

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

如果您确定 x.get_instance() 将 return 是 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())

同样的考虑适用于/确保你只应用这个补丁一次,所以你可能想要添加一个类似的标志内容并测试 class monkeypatch 版本。

最后一点:如果您必须回退到 patch_instance 解决方案并希望确保 all 调用 x.get_instance() return 打补丁的实例,您可能还想打补丁 x.get_instance 以便它调用 patch_instance.

假设你有一个 class Foo 有一个 bar 方法:

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

如果您创建此 class 的实例,名为 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

您可以在不猴子修补 class:

的情况下修补实例
>>> import types

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

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

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

原文class完好:

>>> otherfoo = Foo('foo')

>>> otherfoo.bar()
bar foo