【问题标题】:Python: how to monkey patch class method to other class methodPython:如何将类方法猴子修补到其他类方法
【发布时间】:2016-07-24 02:36:47
【问题描述】:

我有以下代码:

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! ")

我想做以下事情:

一些 A 类由一个带参数的函数组成。 我想将此函数修补到 B 类中的一个函数上做一些操作参数并访问 B 类的变量,我的问题是修补函数实际上需要两个不同的“自我”对象,即 A 类的实例以及实例B类。

这可能吗?

【问题讨论】:

    标签: python monkeypatching


    【解决方案1】:

    问题在于,当您使用已绑定的方法覆盖类函数时,尝试绑定到其他实例时会忽略第二个实例:

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

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

    instA.testfunc(instA,"keep away! ")
    

    我在尝试将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)
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 感谢您的输入,我想在 B 不是 A 的子类的情况下执行此操作,因为我只想对这个单一方法进行修补,而除此之外,B 不需要对 A 做任何事情。我可能需要在我的项目中从 B 中的其他类中修补多个方法,例如 testfunc,这将导致同样的问题...:S
      猜你喜欢
      • 2012-08-19
      • 2016-11-27
      • 2017-02-12
      • 1970-01-01
      • 2021-03-10
      • 2012-05-07
      • 2015-03-23
      • 2015-11-10
      • 2016-11-23
      相关资源
      最近更新 更多