Если вы хотите узнать, почему Python выбрал именно этот алгоритм MRO, обсуждение находится в списке рассылки . архивы и кратко изложены в Порядке разрешения методов Python 2.3.
Но на самом деле все сводится к следующему: разрешение методов Python 2.2 было нарушено при работе с множественным наследованием, и первое, что кто-либо предложил исправить, — это позаимствовать алгоритм C3 у Дилана, и никто не имел с ним никаких проблем и не предлагал ничего лучше, и поэтому Python использует C3.
Если вас больше интересуют общие преимущества (и недостатки) C3 по сравнению с другими алгоритмами…
Ответы BrenBarn и florquake дают основу для этого вопроса. Python super() считается супер! из блога Рэймонда Хеттингера гораздо длиннее и подробнее подробное обсуждение в том же духе, и, безусловно, стоит прочитать.
Монотонная линеаризация суперкласса для Дилана — это оригинальный документ, описывающий дизайн. Конечно, язык Dylan сильно отличается от Python, и это академическая статья, но обоснование все равно довольно хорошее.
Наконец, Порядок разрешения методов Python 2.3 (те же документы, ссылки на которые приведены выше) содержит обсуждение преимуществ .
И вам нужно много узнать об альтернативах и о том, как они подходят и не подходят для Python, чтобы идти дальше. Или, если вам нужна более подробная информация о SO, вам нужно будет задать более конкретные вопросы.
Наконец, если вы задаете вопрос «как»:
Когда вы вызываете D().test()
, очевидно, что он вызывает код, определенный вами в методе test
B
. А B.__mro__
это (__main__.B, __main__.A, object)
. Итак, как этот super(B, self).test()
может вызвать метод test
C
вместо A
?
Ключевым моментом здесь является то, что MRO основан на типе self
, а не на типе B
, где был определен метод test
. Если бы вы print(type(self))
внутри функций test
, вы бы увидели, что это D
, а не B
.
Итак, super(B, self)
на самом деле получает self.__class__.__mro__
(в данном случае D.__mro__
), находит B
в списке и возвращает следующее за ним. Довольно просто.
Но это не объясняет, как работает MRO, а только то, что оно делает. Как D().test()
вызывает метод из B
, но с self
это D
?
Во-первых, обратите внимание, что D().test
, D.test
и B.test
не являются одной и той же функцией, потому что это вообще не функции; это методы. (Я предполагаю, что это Python 2.x. В 3.x все немного по-другому — в основном проще.)
Метод — это, по сути, объект с im_func
, im_class
и im_self
элементами. Когда вы вызываете метод, все, что вы делаете, это вызываете его im_func
с его im_self
(если не None
) вставленным в качестве дополнительного аргумента в начале.
Итак, все три наших примера имеют один и тот же im_func
, который на самом деле является функцией, которую вы определили внутри B
. Но первые два имеют D
, а не B
для im_class
, а первый также имеет экземпляр D
вместо None
для im_self
. Итак, вот как его вызов заканчивается передачей экземпляра D
как self
.
Итак, как D().test
оказывается с этими im_self
и im_class
? Где это создается? Это самое интересное. Полное описание можно найти в Руководстве по дескриптору, но вкратце:
Всякий раз, когда вы пишете foo.bar
, то, что на самом деле происходит, эквивалентно вызову getattr(foo, 'bar')
, который делает что-то вроде этого (игнорируя атрибуты экземпляра, __getattr__
, __getattribute__
, слоты, встроенные функции и т. д.):
def getattr(obj, name):
for cls in obj.__class__.__mro__:
try:
desc = cls.__dict__[name]
except KeyError:
pass
else:
return desc.get(obj.__class__, obj)
Это .get()
в конце — магический бит. Если вы посмотрите на функцию, скажем, B.test.im_func
, вы увидите, что она на самом деле имеет метод get
. И что он делает, так это создает связанный метод с im_func
в качестве самого себя, im_class
в качестве класса obj.__class__
и im_self
в качестве объекта obj
.
person
abarnert
schedule
11.09.2013