python3: привязать метод к экземпляру класса с помощью .__get__(), он работает, но почему?

Я знаю, что если вы хотите добавить метод к экземпляру класса, вы не можете выполнить простое назначение, подобное этому:

>>> def print_var(self): # method to be added
        print(self.var)
>>> class MyClass:
        var = 5
>>> c = MyClass()
>>> c.print_var = print_var

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

>>> c.print_var
<function print_var at 0x98e86ec>
>>> c.print_var()
Traceback (most recent call last):
  File "<pyshell#149>", line 1, in <module>
    c.print_var()
TypeError: print_var() takes exactly 1 argument (0 given)

Чтобы функция считалась методом (т.е. чтобы привязать ее к экземпляру), я использовал этот код:

>>> import types
>>> c.print_var = types.MethodType(print_var, c)
>>> c.print_var
<bound method MyClass.print_var of <__main__.MyClass object at 0x98a1bac>>
>>> c.print_var()
5

но я обнаружил, что .__get__ также может использоваться для этой цели:

>>> c.print_var = print_var.__get__(c)
>>> c.print_var
<bound method MyClass.print_var of <__main__.MyClass object at 0x98a1bac>>
>>> c.print_var()
5

Проблема здесь в том, что это просто работает, но я не могу понять, как и почему. Документация по .__get__ вроде не сильно помогает.

Я был бы признателен, если бы кто-нибудь мог прояснить это поведение интерпретатора Python.


person etuardu    schedule 20.09.2011    source источник


Ответы (1)


Необходимая информация содержится в Руководстве по дескриптору:

Для поддержки вызовов методов функции включают метод __get__() для связывания методов во время доступа к атрибуту. Это означает, что все функции не являются дескрипторами данных, которые возвращают связанные или несвязанные методы в зависимости от того, вызываются ли они из объекта или класса. В чистом Python это работает так:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        return types.MethodType(self, obj)

Так что на самом деле ничего странного не происходит — метод __get__ объекта-функции вызывает types.MethodType и возвращает результат.

person agf    schedule 20.09.2011
comment
В Python 3 MethodType() принимает только 2 аргумента. - person joeforker; 08.07.2016
comment
@joeforker Я цитирую документы Python, похоже, их нужно обновить. - person agf; 08.07.2016
comment
На самом деле они обновляются. Я рекомендую вам отредактировать ответ - person ; 13.09.2018
comment
@J.C.Rocamonde хм... я перешел по ссылке... 3 параметра в документации по python... если только я не правильно понял намерение вашего комментария? - person drevicko; 10.07.2020
comment
@dreviko Он не говорит, что __get__ изменился, он говорит, что MethodType теперь принимает только два аргумента - раньше требовалось три. Я обновил ответ два года назад, чтобы отразить это. - person agf; 11.07.2020