W książce Python in a Nutshell (2nd Edition) znajduje się przykład, który używa
klas starego stylu, aby zademonstrować, w jaki sposób metody są rozwiązywane w klasycznej kolejności i
czym różni się od nowej kolejności.
Wypróbowałem ten sam przykład, przepisując przykład w nowym stylu, ale wynik nie różni się od tego, co uzyskano przy użyciu klas starego stylu. Wersja języka Python, której używam do uruchomienia przykładu, to 2.5.2. Poniżej przykład:
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__
Wywołanie jest instance.amethod()
drukowane Base1
, ale zgodnie z moim zrozumieniem MRO z nowym stylem klas wynik powinien być Base3
. Wezwanie Derived.__mro__
drukuje:
(<class '__main__.Derived'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class '__main__.Base3'>, <type 'object'>)
Nie jestem pewien, czy moje rozumienie MRO z klasami nowych stylów jest niepoprawne, czy też popełniam głupi błąd, którego nie jestem w stanie wykryć. Proszę, pomóż mi w lepszym zrozumieniu MRO.
źródło
Kolejność rozwiązywania metod w Pythonie jest w rzeczywistości bardziej złożona niż samo zrozumienie wzoru rombu. Aby naprawdę to zrozumieć, spójrz na linearyzację C3 . Zauważyłem, że naprawdę pomaga użycie instrukcji print podczas rozszerzania metod śledzenia zamówienia. Na przykład, jak myślisz, jaki byłby wynik tego wzorca? (Uwaga: przypuszcza się, że 'X' to dwie przecinające się krawędzie, a nie węzeł, a ^ oznacza metody, które wywołują 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
Czy otrzymałeś ABDCEFG?
Po wielu próbach i błędach wpadłem na nieformalną interpretację teorii grafów linearyzacji C3 w następujący sposób: (Niech ktoś mi powie, jeśli to jest błędne).
Rozważmy ten przykład:
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()
źródło
super
ma wymagane argumenty.Otrzymany wynik jest prawidłowy. Spróbuj zmienić klasę bazową
Base3
naBase1
i porównaj z tą samą hierarchią klas klasycznych: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()
Teraz wyświetla:
Przeczytaj to wyjaśnienie, aby uzyskać więcej informacji.
źródło
Widzisz to zachowanie, ponieważ rozdzielczość metody jest najpierw głęboka, a nie szerokość. Jak wygląda spadek po Dervied
Więc
instance.amethod()
amethod
, więc zostaje wywołany.Znajduje to odzwierciedlenie w
Derived.__mro__
. Po prostu powtórzDerived.__mro__
i zatrzymaj się, gdy znajdziesz poszukiwaną metodę.źródło