Ответ 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 полон косвенности и делегирования, что делает его настолько гибким.
Надеюсь, что это немного изменит ситуацию.