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

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

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

Это возможно?


person Dennis van den Berg    schedule 04.04.2016    source источник


Ответы (2)


проблема в том, что когда вы переопределяете функцию класса с уже связанным методом, пытаясь привязать к другим экземплярам, ​​просто игнорируете второй экземпляр:

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)
person Tadhg McDonald-Jensen    schedule 04.04.2016

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

class B(A):
    def __init__(self):
        A.__init__(self)
        # Otherwise the same
person user1747134    schedule 04.04.2016
comment
Спасибо за ваш вклад, я хочу сделать это так, чтобы B не был подклассом A, так как я хочу только обезьяны исправлять этот единственный метод, а B не должен ничего делать в отношении A, кроме этого. Мне, вероятно, нужно исправить несколько методов, таких как testfunc, из других классов внутри B в моем проекте, что приведет к той же проблеме ...: S - person Dennis van den Berg; 04.04.2016