Если вы перестанете думать об этом, это просто интуитивный способ работы. Эта статья , которая на данный момент выглядит просто как археологическая находка, все еще является авторитетным описанием и аргументацией алгоритма порядка разрешения методов Python.
Но, несмотря на все технические детали, в ваших двух примерах:
В первом, D,B,C,A
, путь через B указывает, что следует использовать атрибут A
. Но сам атрибут A
затеняется атрибутом в C, то есть объявление в C переопределяет attr
, объявленное в A
. Поэтому это тот, который используется.
Во второй иерархии, D,B,C,A,E
, B, идущий первым, чем C, снова указывает, что следует использовать A.attr
. На этот раз, однако, собственный атрибут A не был скрыт другим классом в иерархии - скорее, C.attr происходит из другой «линии» - поэтому язык выбирает первое, с чем сталкивается.
Это «простое английское описание» того, что происходит. В приведенной выше авторитетной статье изложены формальные правила:
линеаризация [класса] C является суммой C плюс слияние
линеаризации родителей и список родителей.
...
[заданный класс C (B1, ..., BN):], взять голову первого списка, то есть L [B1] [0] [линеаризация (aka mro) базы B1 до объекта - голова B1 - ]; если эта голова не в
хвост любого из других списков [списков линеаризации для других базисов], затем добавьте его к линеаризации
C и удалите его из списков в слиянии, в противном случае посмотрите на
возглавьте следующий список и возьмите его, если это хорошая голова. Затем повторите
операция, пока все классы не будут удалены или невозможно
найти хорошие головы. В этом случае невозможно построить
merge, Python 2.3 [и последующие версии] откажется создавать класс C и поднимет
исключение.
Приведя второй пример, вы получите
D(B, C)
- линеаризации для B и C: [B, A, object]
и [C, E, object]
, и линеаризация D начинается с взятия «B», проверяя, не
на хвосте любых других списков (и это не на [C, E, объект]), то берется B. Остальные списки [A, object]
и [C, E, object]
- алгоритм выбирает A
, которого нет в другом списке, затем A
добавляется к значению D. Затем выбирается object
. Это - это в другом списке. Таким образом, алгоритм оставляет первый список нетронутым и принимает C, E и, наконец, объект для D, B, A, C, E, object
линеаризации.
В вашем первом примере линеаризация обеих баз равна [B, A, object]
и [C, A, object]
, когда алгоритм проверяет на A
, это в хвосте второго списка - так, C
выбирается первым, чем A
из второго списка - окончательная линеаризация D, B, C, A, object
.