Если вы должны были запустить 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()
- ли
b
есть атрибут под названием a
? Да; мы установили его в B.__init__
.
- У
b.a
есть атрибут method_a
? №
type(b.a)
(то есть A
) есть атрибут method_a
? Да.
- Звоните
A.method_a.__get__(b.a, A)
, так как method_a
был найден объект. Результатом является объект метода экземпляра, первый аргумент которого связан с b.a
. (Вот почему вы можете рассмотреть b.a.method_a()
, идентичный A.method_a(b.a)
).
- Вызвать полученный метод экземпляра с нулевыми аргументами.
Теперь рассмотрим этот код.
b = B()
b.a.method_a = my_method_a
b.a.method_a()
- ли
b
есть атрибут называется a
? Да; мы установили его в B.__init__
.
- У
b.a
есть атрибут method_a
? Да. Мы установили его перед тем, как мы попытались это назвать.
- С
b.a.method_a
был поиск экземпляра, а не поиск класса, протокол дескриптора не вызывается, а b.a.method_a.__get__
не вызывается, хотя my_method_a
имеет функцию __get__
, как и любую другую функцию.
Звоните b.a.method_a
с нулевыми аргументами.
Это вызывает ошибку, так как функция ожидает один аргумент.
почему это считается приемлемым - я бы ожидать надлежащего наследования и метод переопределяет бы предпочтительнее и более понятной и ремонтопригодны – gkusner
К заплат 'ba' , вы уже сломали наследство; 'b.a' больше не ведет себя так же, как другие экземпляры типа' A'. – chepner
Я имел в виду делать оба класса – gkusner