2016-02-25 16 views
11

Przeczytałem programowanie w języku Python, które ma przykład dla dziedziczenia wielokrotnego. Autor książki wyjaśnił, ale ja tego nie zrozumiałem, więc chciałbym mieć inny pogląd.Obiekt jest tworzony dwa razy w Pythonie

Przykład pokazuje, że obiekt B został utworzony dwa razy!

Czy możesz podać mi intuicyjne wyjaśnienie.

In [1]: class A(object): 
    ...:  def __init__(self): 
    ...:   print "A" 
    ...:   super(A, self).__init__() 

In [2]: class B(object): 
    ...:  def __init__(self): 
    ...:   print "B" 
    ...:   super(B, self).__init__() 

In [3]: class C(A,B): 
    ...:  def __init__(self): 
    ...:   print "C" 
    ...:   A.__init__(self) 
    ...:   B.__init__(self) 

In [4]: print "MRO:", [x.__name__ for x in C.__mro__] 
MRO: ['C', 'A', 'B', 'object'] 

In [5]: C() 
C 
A 
B 
B 
Out[5]: <__main__.C at 0x3efceb8> 

Autor książki powiedział:

Dzieje się tak ze względu na wezwanie A.__init__(self), która jest wykonana z instancji C, dzięki czemu super(A, self).__init__() połączeń B „s konstruktora

punktem, z którego nie wpadłem na pomysł, jest to, w jaki sposób wywołanie A.__init__(self) wywoła super(A, self).__init__() wywołanie 's konstruktora

+0

Czemu nie używając 'Super 'in C? metoda init? –

+0

Chcę tylko zrozumieć, jak obiekt jest tworzony w Pythonie. – Bryan

+3

Jeśli uważasz, że "super" nazywa się "wywołaj następną metodę w MRO", zamiast "wywołaj moją metodę klasy nadrzędnej", wtedy to zachowanie powinno mieć więcej sensu. W tym przypadku powód, dla którego B jest drukowany dwa razy, jest taki, że super już przygotowuje się do wywołania inicjalizatora B (przez init A), więc kiedy jawnie wywołasz B.init z C, otrzymujesz drugie połączenie. –

Odpowiedz

8

Numer super() oznacza "następny w linii", gdzie linia jest mro['C', 'A', 'B', 'object']. Następna linia A to B.

Mro jest obliczane zgodnie z algorytmem o nazwie C3 linearization. Podczas korzystania z super(), Python po prostu wykonuje tę kolejność. Kiedy piszesz swoją klasę A, nie wiesz jeszcze, która klasa będzie następna w kolejce. Dopiero po utworzeniu klasy C z wieloma dziedziczeniami i uruchomieniu programu otrzymasz mro i "dowiesz się", co będzie następne dla A.

Dla przykładu oznacza to:

C() wywołuje __init__() z C, w którym nazywa go __init__() z A. Teraz A używa super() i znajduje B w mro, stąd nazywa się __init__() z B. Następnie ponownie wywołuje __init__() z C.

Wywołanie super() w __init__() tworzy inny MRO i unika podwójnego połączenia do __init__() z B.

from __future__ import print_function 

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") 
     super(C, self).__init__() 

Zastosowanie:

>>> C.mro() 
[__main__.C, __main__.A, __main__.B, object] 
>> C() 
C 
A 
B 
+1

Ciągle nie rozumiem, czy możesz wyjaśnić więcej? – danidee

+0

C MRO to C [, C, A, B, obiekt]. C nazywa 'A .__ init __()', A wywołuje 'super()', w C MRO (gdzie wywołano 'A .__ init __()' następnie w linii jest 'B', więc 'print 'B'' jest wywołane w A's super() i w 'B .__ init __()' –

1

zmieńmy kod trochę i zastąpić __init__ z doit tylko upewnić się, że zachowanie ma charakter ogólny i nie związane z __init__.

Załóżmy również dodać więcej wyjście, aby zobaczyć, co dokładnie się dzieje:

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

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

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

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

C().doit() 

Wyjście to będzie:

C <__main__.C object at ...> 
A <__main__.C object at ...> <super: <class 'A'>, <C object>> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 

Widać, że self jest rzeczywiście C obiekt wszędzie, więc gdy trafisz A.doit , faktycznie masz <super: <class 'A'>, <C object>>.

co przekłada się na:

dla obiektu C wywołać metodę następnego (Super) doit klasy po A z listy MRO

aw następnej klasy w MRO po A to B, więc kończymy dzwoniąc pod numer B.doit().

Sprawdź również ten kod:

class C(A,B): 

    def doit_explain(self): 
     print "C", self 
     # calls B.doit() 
     super(A, self).doit() 
     print "Back to C" 
     # calls A.doit() (and super in A also calls B.doit()) 
     super(C, self).doit() 
     print "Back to C" 
     # and just B.doit() 
     B.doit(self) 

Tutaj zamiast A.doit(self) używam super(A, self).doit() bezpośrednio i to również powoduje B.doit() rozmowy, tu jest wyjście:

C <__main__.C object at ...> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 
Back to C 
A <__main__.C object at ...> <super: <class 'A'>, <C object>> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 
Back to C 
B <__main__.C object at ...> <super: <class 'B'>, <C object>>