Порядок разрешения методов в питоне - PullRequest
2 голосов
/ 09 июля 2019

Я новичок в питоне.Я использую Python 2.7.Я проходил процедуру разрешения порядка с помощью небольшого фрагмента следующим образом:

class A(object):
    attr = 'A'

class B(A):
    pass

class C(A):
    attr = 'C'

class D(B,C):
    pass

x = D()
print x.attr

Порядок разрешения равен x, D, B, C, A, и, следовательно, на выходе получилось бы C. Исходя из приведенного выше примераЯ внес небольшое изменение в код.

class A(object):
    attr = 'A'

class E(object):
    attr = 'E'

class B(A):
    pass

class C(E):
    attr = 'C'

class D(B,C):
    pass

x = D()
print x.attr

Исходя из моего предыдущего примера, я ожидал, что порядок будет x, D, B, C, A, E.К моему удивлению, результат был «А».Поэтому я запутался с порядком разрешения в классе нового стиля.Может кто-нибудь уточнить, когда к родительскому классу B обращались до класса C.Спасибо.

Ответы [ 2 ]

0 голосов
/ 09 июля 2019

Если вы перестанете думать об этом, это просто интуитивный способ работы. Эта статья , которая на данный момент выглядит просто как археологическая находка, все еще является авторитетным описанием и аргументацией алгоритма порядка разрешения методов 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.

0 голосов
/ 09 июля 2019

Это из-за порядка, поэтому, если D равно:

class D(C,B):
    pass

Будет выведено C, поскольку он получает атрибут attr первого унаследованного класса.

...