2015-12-01 3 views
1

Я хочу изменить определение метода класса. Это мой случай:Изменение метода определения

(я импортировать эти классы из другого файла)

class A(object): 
    def __init__(self, str): 
     self.str = str 

    def method_a(self): 
     print self.str 


class B(object): 
    def __init__(self, str): 
     self.a = A(str) 

    def method_b(self): 
     self.a.method_a() 

####################################### 

from module import A, B 

def main(): 
    b = B('hello') 

    def my_method_a(self): 
     print self.str + 'other definition' 

    b.a.method_a = my_method_a 
    b.method_b() 

if __name__ == '__main__': 
    main() 

Когда я пытаюсь выполнить его, я получаю:

my_method_a() takes exactly 1 argument (0 given) 

Потому что он не получает " самость».

Любая помощь пожалуйста.

ответ

2

Если вы должны были запустить type(b.a.method_a) перед тем, как исправить метод, вы увидите <type 'instancemethod'>. Запуск того же кода после исправления дает <type 'function'>. Для того, чтобы функция работала корректно как метод, она должна быть атрибутом класса , а не экземпляром класса. Ниже будет работать, как вы вручную вызов магии, которая производит метод из функции:

b.a.method_a = my_method_a.__get__(b.a, A) 

См https://wiki.python.org/moin/FromFunctionToMethod для получения дополнительной информации.


Разница заключается в том, что при вызове b.a.method_a() после патча, method_a является атрибутом экземпляраb.a, а не из классаA. В результате метод функции __get__ никогда не вызывается для создания объекта instancemethod, который уже имеет b.a, связанный с первым аргументом method_a.


С одной стороны, b.a.method_a() идентичен A.method_a(b.a). Как Python делает этот переход? Вам нужно понять протокол дескриптора. Все объекты функций реализуют метод __get__ для возврата объекта instancemethod, который вы можете представить как исходную функцию с первым аргументом, привязанным к соответствующему объекту. Рассмотрим этот код:

b = B() 
b.a.method_a() 
  1. ли b есть атрибут под названием a? Да; мы установили его в B.__init__.
  2. У b.a есть атрибут method_a? №
  3. type(b.a) (то есть A) есть атрибут method_a? Да.
  4. Звоните A.method_a.__get__(b.a, A), так как method_a был найден объект. Результатом является объект метода экземпляра, первый аргумент которого связан с b.a. (Вот почему вы можете рассмотреть b.a.method_a(), идентичный A.method_a(b.a)).
  5. Вызвать полученный метод экземпляра с нулевыми аргументами.

Теперь рассмотрим этот код.

b = B() 
b.a.method_a = my_method_a 
b.a.method_a() 
  1. ли b есть атрибут называется a? Да; мы установили его в B.__init__.
  2. У b.a есть атрибут method_a? Да. Мы установили его перед тем, как мы попытались это назвать.
  3. С b.a.method_a был поиск экземпляра, а не поиск класса, протокол дескриптора не вызывается, а b.a.method_a.__get__ не вызывается, хотя my_method_a имеет функцию __get__, как и любую другую функцию.
  4. Звоните b.a.method_a с нулевыми аргументами.

    Это вызывает ошибку, так как функция ожидает один аргумент.

+0

почему это считается приемлемым - я бы ожидать надлежащего наследования и метод переопределяет бы предпочтительнее и более понятной и ремонтопригодны – gkusner

+0

К заплат 'ba' , вы уже сломали наследство; 'b.a' больше не ведет себя так же, как другие экземпляры типа' A'. – chepner

+0

Я имел в виду делать оба класса – gkusner

0

почему бы просто не использовать наследование и метод переопределения:

from module import A, B 

class myA(A): 
    def method_a(self): 
     print self.str + ' other definition' 

class myB(B): 
    def __init__(self, str): 
     self.a = myA(str) 

def main(): 
    b = myB('hello') 
    b.method_b() 

if __name__ == '__main__': 
    main() 
Смежные вопросы