В книге « Python в двух словах» (2-е издание) есть пример, в котором используются
классы старого стиля для демонстрации того, как методы разрешаются в классическом порядке разрешения и чем
он отличается от нового порядка.
Я попробовал тот же пример, переписав его в новом стиле, но результат не отличается от того, что был получен с классами старого стиля. Версия python, которую я использую для запуска примера, - 2.5.2. Ниже приведен пример:
class Base1(object):
def amethod(self): print "Base1"
class Base2(Base1):
pass
class Base3(object):
def amethod(self): print "Base3"
class Derived(Base2,Base3):
pass
instance = Derived()
instance.amethod()
print Derived.__mro__
Вызов instance.amethod()
выводится Base1
, но, согласно моему пониманию MRO с новым стилем классов, результат должен был быть Base3
. Вызов Derived.__mro__
распечатывает:
(<class '__main__.Derived'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class '__main__.Base3'>, <type 'object'>)
Я не уверен, что мое понимание MRO с новыми классами стилей неверно или я делаю глупую ошибку, которую не могу обнаружить. Пожалуйста, помогите мне лучше понять MRO.
Порядок разрешения методов Python на самом деле более сложен, чем просто понимание ромбовидного узора. Чтобы действительно понять это, взгляните на С3 линеаризацию . Я обнаружил, что использование операторов печати действительно помогает при расширении методов для отслеживания порядка. Например, как вы думаете, каким будет результат этого шаблона? (Примечание: 'X' предполагает два пересекающихся ребра, а не узел, а ^ означает методы, которые вызывают super ())
class G(): def m(self): print("G") class F(G): def m(self): print("F") super().m() class E(G): def m(self): print("E") super().m() class D(G): def m(self): print("D") super().m() class C(E): def m(self): print("C") super().m() class B(D, E, F): def m(self): print("B") super().m() class A(B, C): def m(self): print("A") super().m() # A^ # / \ # B^ C^ # /| X # D^ E^ F^ # \ | / # G
Вы получили ABDCEFG?
После многих проб и ошибок я придумал неформальную интерпретацию линеаризации C3 теорией графов следующим образом: (Кто-нибудь, пожалуйста, дайте мне знать, если это неправильно.)
Рассмотрим этот пример:
class I(G): def m(self): print("I") super().m() class H(): def m(self): print("H") class G(H): def m(self): print("G") super().m() class F(H): def m(self): print("F") super().m() class E(H): def m(self): print("E") super().m() class D(F): def m(self): print("D") super().m() class C(E, F, G): def m(self): print("C") super().m() class B(): def m(self): print("B") super().m() class A(B, C, D): def m(self): print("A") super().m() # Algorithm: # 1. Build an inheritance graph such that the children point at the parents (you'll have to imagine the arrows are there) and # keeping the correct left to right order. (I've marked methods that call super with ^) # A^ # / | \ # / | \ # B^ C^ D^ I^ # / | \ / / # / | X / # / |/ \ / # E^ F^ G^ # \ | / # \ | / # H # (In this example, A is a child of B, so imagine an edge going FROM A TO B) # 2. Remove all classes that aren't eventually inherited by A # A^ # / | \ # / | \ # B^ C^ D^ # / | \ / # / | X # / |/ \ # E^ F^ G^ # \ | / # \ | / # H # 3. For each level of the graph from bottom to top # For each node in the level from right to left # Remove all of the edges coming into the node except for the right-most one # Remove all of the edges going out of the node except for the left-most one # Level {H} # # A^ # / | \ # / | \ # B^ C^ D^ # / | \ / # / | X # / |/ \ # E^ F^ G^ # | # | # H # Level {G F E} # # A^ # / | \ # / | \ # B^ C^ D^ # | \ / # | X # | | \ # E^F^ G^ # | # | # H # Level {D C B} # # A^ # /| \ # / | \ # B^ C^ D^ # | | # | | # | | # E^ F^ G^ # | # | # H # Level {A} # # A^ # | # | # B^ C^ D^ # | | # | | # | | # E^ F^ G^ # | # | # H # The resolution order can now be determined by reading from top to bottom, left to right. A B C E D F G H x = A() x.m()
источник
super
имеет необходимые аргументы.Полученный результат правильный. Попробуйте изменить базовый класс
Base3
наBase1
и сравнить с той же иерархией для классических классов:class Base1(object): def amethod(self): print "Base1" class Base2(Base1): pass class Base3(Base1): def amethod(self): print "Base3" class Derived(Base2,Base3): pass instance = Derived() instance.amethod() class Base1: def amethod(self): print "Base1" class Base2(Base1): pass class Base3(Base1): def amethod(self): print "Base3" class Derived(Base2,Base3): pass instance = Derived() instance.amethod()
Теперь он выводит:
Прочтите это объяснение для получения дополнительной информации.
источник
Вы наблюдаете такое поведение, потому что разрешение метода ориентировано на глубину, а не в ширину. Наследование Dervied выглядит как
Так
instance.amethod()
amethod
, поэтому он вызывается .Это отражено в
Derived.__mro__
. Просто повторитеDerived.__mro__
и остановитесь, когда найдете искомый метод.источник