2016-04-04 5 views
2

я получил следующий код: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! ") 

Я хочу сделать следующее:

Некоторого класс А состоит из функции с аргументами. Я хочу, чтобы обезьяна заплатила эту функцию функции в классе B, некоторые манипулируют аргументами и получают доступ к переменным класса B, моя проблема, являющаяся исправленной функцией, действительно требует двух разных «я» объектов, а именно экземпляра класса A, а также экземпляр класса B.

Возможно ли это?

ответ

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

Если B может быть подклассом A, проблема будет решена.

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

Спасибо за ваш вклад, я хочу сделать это без B является подклассом, так как я только хочу, чтобы обезьяна патч этот единственный метод и B не должен делать что-нибудь рег кроме того. Мне, вероятно, нужно обезвредить несколько методов, таких как testfunc из других классов внутри B в моем проекте, что приведет к той же проблеме ...: S –

Смежные вопросы