Python:如何将类方法monkey补丁到其他类方法

2024-10-01 13:41:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下代码:

class A:
    def __init__(self):
        self.a = "This is mine, "

    def testfunc(self, arg1):
        print self.a + arg1

class B:
    def __init__(self):
        self.b = "I didn't think so"
        self.oldtestfunc = A.testfunc
        A.testfunc = self.testfuncPatch

    def testfuncPatch(self, arg):
        newarg = arg + self.b # B instance 'self'
        self.oldtestfunc(self, newarg) # A instance 'self'

instA = A()
instB = B()
instA.testfunc("keep away! ")

我想做以下事情:

有些类是由带参数的函数组成的。 我想把这个函数monkey补丁到类B中的一个函数上,做一些操作参数和访问类B的变量,我的问题是修补后的函数实际上需要两个不同的“self”对象,即类a的实例和类B的实例

这可能吗?在


Tags: 实例instance函数self参数initdefarg
2条回答

如果B可以是A的子类,那么问题就可以解决了。在

class B(A):
    def __init__(self):
        A.__init__(self)
        # Otherwise the same

问题是,当您使用已经绑定的方法重写类函数时,尝试绑定到其他实例时,只需忽略第二个实例:

print(instA.testfunc)
#<bound method B.testfuncPatch of <__main__.B object at 0x1056ab6d8>>

因此,该方法基本上被视为staticmethod,这意味着您必须以实例作为第一个参数来调用它:

^{pr2}$

当我试图将random.shuffle直接导入一个类以使其成为一个方法时,我第一次遇到这个问题:

class List(list):
    from random import shuffle #I was quite surprised when this didn't work at all

a = List([1,2,3])
print(a.shuffle)
#<bound method Random.shuffle of <random.Random object at 0x1020c8c18>>
a.shuffle()

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/codes/test.py", line 5, in <module>
    a.shuffle()
TypeError: shuffle() missing 1 required positional argument: 'x'

为了解决这个问题,我创建了一个函数,它可以在第一个实例的基础上反弹到第二个实例:

from types import MethodType

def rebinder(f):
    if not isinstance(f,MethodType):
        raise TypeError("rebinder was intended for rebinding methods")
    def wrapper(*args,**kw):
        return f(*args,**kw)
    return wrapper

class List(list):
    from random import shuffle
    shuffle = rebinder(shuffle) #now it does work :D

a = List(range(10))
print(a.shuffle)
a.shuffle()
print(a)

#output:
<bound method rebinder.<locals>.wrapper of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>
[5, 6, 8, 2, 4, 1, 9, 3, 7, 0]

因此,您可以很容易地将此应用于您的情况:

from types import MethodType

def rebinder(f):
    if not isinstance(f,MethodType):
        raise TypeError("rebinder was intended for rebinding methods")
    def wrapper(*args,**kw):
        return f(*args,**kw)
    return wrapper
...

class B:
    def __init__(self):
        self.b = "I didn't think so"
        self.oldtestfunc = A.testfunc
        A.testfunc = rebinder(self.testfuncPatch) #!! Edit here

    def testfuncPatch(selfB, selfA, arg): #take the instance of B first then the instance of A
        newarg = arg + selfB.b
        self.oldtestfunc(selfA, newarg)

相关问题 更多 >