2010-09-17 13 views
9

po pierwsze, pozwól mi przytoczyć nieco esej z „Expert Python Programming” książki:mieszania Super i klasyczne połączenia w Pythonie

W poniższym przykładzie, klasy C, który wzywa swoich klas bazowych za pomocą metody __init__ będzie sprawić, że klasa B zostanie wywołana dwa razy!

class A(object): 
    def __init__(self): 
     print "A" 
     super(A, self).__init__() 

class B(object): 
    def __init__(self): 
     print "B" 
     super(B, self).__init__() 

class C(A,B): 
    def __init__(self): 
     print "C" 
     A.__init__(self) 
     B.__init__(self) 

print "MRO:", [x.__name__ for x in C.__mro__] #prints MRO: ['C', 'A', 'B', 'object'] 
C() #prints C A B B 

i wreszcie, tutaj jest wyjaśnienie tego, co się dzieje tutaj:

Dzieje się tak ze względu na A .__ init, __ (samo) rozmowy, który jest wykonany z instancją C, w ten sposób tworząc super (A, self) .__ init __() wywołanie B konstruktora. Innymi słowy, super powinno być użyte w całej hierarchii klas. Problem polega na tym, że czasami część tej hierarchii znajduje się w kodzie strony trzeciej.

Nie mam pojęcia, dlaczego "super(A, self).__init__() dzwoni do konstruktora B". Proszę wyjaśnij tę chwilę. Wielkie dzięki.

Odpowiedz

4

Dokumentacja super mówi, że:

Return obiekt proxy, który deleguje metoda nazywa się rodzicem lub rodzeństwem klasy typu. Jest to przydatne do uzyskiwania dostępu do odziedziczonych metod, które zostały nadpisane w klasie. Kolejność wyszukiwania jest taka sama, jak w przypadku getattr(), z wyjątkiem tego, że sam typ jest pomijany.

Po wykonaniu A.__init__(self) od wewnątrz Csuper(A, self) powróci <super: <class 'A'>, <C object>>. Jako instancji jest C (<C object>) wszystkie klasy w hierarchii dziedziczenia C są pobierane. Na wszystkich jest wywoływane __init__. W konsekwencji widzisz, że "B" jest wywoływany dwa razy.

Aby to zweryfikować, dodaj jeszcze inną klasę "Z", a "C" również dziedziczy z "Z". Zobacz co się dzieje.

class Z(object): 
    def __init__(self): 
     print "Z" 
     super(Z, self).__init__() 

class C(A, B, Z):  
    def __init__(self): 
     print "C" 
     A.__init__(self) 
     B.__init__(self) 
     Z.__init__(self) 

W tym przypadku A wezwie B i Z. B będzie również wywoływać Z.

9

Aby zrozumieć to zachowanie, należy zrozumieć, że super nie wywołuje klasy bazowej, ale przeszukuje kolejną pasującą metodę zgodnie z kolejnością w __mro__. Tak więc wywołanie super(A, self).__init__() patrzy na __mro__ == ['C', 'A', 'B', 'object'], widzi B jako następną klasę z pasującą metodą i wywołuje metodę (konstruktor) z B.

Jeżeli zmienisz C do

class C(A,B): 
    def __init__(self): 
     print "C1" 
     A.__init__(self) 
     print "C2" 
     B.__init__(self) 
     print "C3" 

masz

MRO: ['C', 'A', 'B', 'object'] 
C1 
A 
B 
C2 
B 
C3 

który pokazuje, jak konstruktor A połączeń B.

+1

dzięki. jedno pytanie - czy to prawda, że ​​kiedy "super (A, self) .__ init __()" zostaje wywołane w konstruktorze klasy A, jego argument "self" jest równy naszemu instancji C, którą właśnie stworzyliśmy? – varnie

+2

@varnie: tak. Możesz 'print super (A, self)' wewnątrz metody '__init __()' A, aby zobaczyć, że 'self' jest rzeczywiście instancją' C'. –

Powiązane problemy