Подтвердить что ты не робот

Instancemethod или функция?

Сегодня я определил класс для тестирования следующего:

class B(object):
    def p(self):
        print("p")

И позже я сделал это:

>>> type(B.__dict__['p'])
<type 'function'>
>>> type(B.p)
<type 'instancemethod'>

Итак, почему? Не B.p и B.__dict__['p'] тот же объект?

Мое удивление только увеличилось, когда я пробовал это:

>>> B.__dict__['p']
<function p at 0x3d2bc80>
>>> type(B.__dict__['p'])
<type 'function'>

Хорошо, насколько это хорошо, тип находится в обоих результатах function, но когда я попытался:

>>> B.p
<unbound method B.p>
>>> type(B.p)
<type 'instancemethod'>

Что?!, Почему? unbound method и instancemethod? То же самое? Почему два разных имени?

Ну, похоже, python полон сюрпризов!

И это питон, который я использую:

Python 2.7.4 (default, Sep 26 2013, 03:20:26) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
4b9b3361

Ответ 1

Это очень хороший вопрос.

Чтобы очистить несколько вещей:

  • При вызове имени метода через его класс (например, B.p) он говорит, что он не связан.
  • В противном случае доступ к способу через экземпляр класса (например, B().p) называется связанным.

Основное различие между связанным и несвязанным состоит в том, что если привязано, первый аргумент будет неявным экземпляром класса вызывающего. Поэтому нам нужно добавить self в качестве первого аргумента для каждого метода, когда мы его определяем. При несвязании вам придется явно передать экземпляр класса, к которому вы хотите применить логику метода.

Например:

class B(object):
    def foo(self):
        print 'ok'

>>>B().foo()
ok
>>>B.foo()
Exception, missing an argument.
>>>B.foo(B())
ok

Это основное объяснение связанного и несвязанного. Теперь о странности __dict__. Любой объект в Python может определять метод __get__ и __set__, который контролирует доступ к ним, когда они являются атрибутами в классе. Они называются дескрипторами.

В более простых словах, когда вы получаете доступ к атрибуту (свойству) класса по его экземпляру или классу, Python не возвращает объект напрямую, вместо этого он вызывает метод __get__ или __set__, который, в свою очередь, возвращает удобный объект для работы с.

Функции в Python переопределяют этот метод __get__. Поэтому, когда вы выдаете B.foo или B().foo, он возвращает тип instancemethod, который является оболочкой типа function (оболочка, которая неявно передает self как первый аргумент). Когда вы обращаетесь к функции через словарь исходного класса, нет вызова __get__, потому что вы не обращаетесь к ним как к свойству класса, поэтому возвращаемое значение является необработанной функцией.

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

ОБНОВЛЕНИЕ: о вашем последнем примере:

>>> B.p
<unbound method B.p>
>>> type(B.p)
<type 'instancemethod'>

Обратите внимание, что в интерпретаторе Python >>>B.p фактически не печатается тип объекта, вместо этого он печатает объект __repr__. Вы можете проверить это, выполнив >>>print B.p.__repr__() и увидев, что это тот же результат:)

Python полон косвенности и делегирования, что делает его настолько гибким.

Надеюсь, что это немного изменит ситуацию.

Ответ 2

Итак, почему? Не B.p и B.__dict__['p'] тот же объект?

Нет. Доступ к атрибуту - магия; когда пользовательский метод получает доступ по имени атрибута, возвращается метод привязки или несвязанный метод (это изменяется в Python 3, больше не существует несвязанных методов, вы получаете регулярную функцию, если вы обращаетесь к методу экземпляра через объект класса); доступ непосредственно через dict обходит эту магию.

Что?!, Почему? unbound method и instancemethod?, То же самое? Почему два разных имени?

Пользовательский метод имеет тип instancemethod, если он не был определен с помощью декоратора @classmethod или @staticmethod. Это может быть связанный метод экземпляра (при доступе как атрибут экземпляра) или несвязанный метод (при доступе как атрибут класса).

(см. раздел "Пользовательские методы" http://docs.python.org/2/reference/datamodel.html для объяснения.)

Ответ 3

Итак, почему? Не являются ли B.p и B. dict ['p'] одним и тем же объектом?

Это не так. Прочтите следующее: https://wiki.python.org/moin/FromFunctionToMethod для более подробных объяснений.