2016-01-19 36 views
14

Rozważmy następujący fragment kodu Pythonapython wiele argumentów dziedziczenie przejściu do konstruktorów wykorzystujące Super

class A(object): 
    def __init__(self, a): 
     self.a = a 

class B(A): 
    def __init__(self, a, b): 
     super(B, self).__init__(a) 
     self.b = b 

class C(A): 
    def __init__(self, a, c): 
     super(C, self).__init__(a) 
     self.c = c 

class D(B, C): 
    def __init__(self, a, b, c, d): 
     #super(D,self).__init__(a, b, c) ??? 
     self.d = d 

Zastanawiam się w jaki sposób można przekazać a, b i c do konstruktorów odpowiadającej klas bazowych.

Dzięki,

+5

Możliwy duplikat [W jaki sposób działa super() Pythona w przypadku dziedziczenia wielokrotnego?] (Http://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-initance) – erip

+0

ty można dwukrotnie wywołać '__init__' dla' B' i 'C'. – sobolevn

+0

Ale wtedy 'A .__ init__' będzie wywoływany dwa razy, prawda? – user5459381

Odpowiedz

22

Cóż, jeśli mamy do czynienia z dziedziczeniem wielokrotnym ogólnie, wasze klasy bazowe (niestety) powinny być przeznaczone do dziedziczenia wielokrotnego. Klasy B i C w twoim przykładzie nie są, a tym samym nie możesz znaleźć odpowiedniego sposobu zastosowania super w D.

Jednym z najczęstszych sposobów projektowania swoich klas bazowych do wielokrotnego dziedziczenia, jest dla klas bazowych średnim poziomie przyjąć dodatkowe argumenty w swojej metodzie __init__, których nie zamierzamy skorzystać, i przekazać je wzdłuż do ich super połączenie.

Oto jeden ze sposobów, aby to zrobić w Pythonie:

class A(object): 
    def __init__(self,a): 
     self.a=a 

class B(A): 
    def __init__(self,b,**kw): 
     self.b=b 
     super(B,self).__init__(**kw) 

class C(A): 
    def __init__(self,c,**kw): 
     self.c=c 
     super(C,self).__init__(**kw) 

class D(B,C): 
    def __init__(self,a,b,c,d): 
     super(D,self).__init__(a=a,b=b,c=c) 
     self.d=d 

Może to być postrzegane jako rozczarowujące, ale to jest po prostu tak jest.

+1

Nie musisz pisać "kw.pop (" coś ")', możesz po prostu wziąć 'coś' jako argument. – GingerPlusPlus

+0

@GingerPlusPlus w prawo! naprawione, dzięki. (Zacząłem pisać z myślą o argumentach dotyczących pozycji, a następnie przełączałem na kwargs ...) – shx2

+1

Dzięki, shx2, fajne rozwiązanie! – Ling

3

Niestety, nie ma sposobu, aby to robić przy użyciu super() bez zmiany klas bazowych. Wszelkie wywołanie konstruktorów dla B lub C ma zamiar spróbować zadzwonić do następnej klasy w Method Resolution Order, który zawsze będzie B lub C zamiast klasy A że klasa konstruktorzy B i C przyjąć.

Alternatywą jest jawne wywoływanie konstruktorów bez użycia super() w każdej klasie.

class A(object): 
    def __init__(self, a): 
     object.__init__() 
     self.a = a 

class B(A): 
    def __init__(self, a, b): 
     A.__init__(self, a) 
     self.b = b 

class C(object): 
    def __init__(self, a, c): 
     A.__init__(self, a) 
     self.c = c 

class D(B, C): 
    def __init__(self, a, b, c, d): 
     B.__init__(self, a, b) 
     C.__init__(self, a, c) 
     self.d = d 

Jest jeszcze minusem tutaj jako konstruktor A byłoby nazwać dwukrotnie, których tak naprawdę nie ma większego wpływu w tym przykładzie, ale może powodować problemy w bardziej złożonych konstruktorów. Możesz dołączyć sprawdzanie, aby uniemożliwić działanie konstruktora więcej niż jeden raz.

class A(object): 
    def __init__(self, a): 
     if hasattr(self, 'a'): 
      return 
     # Normal constructor. 

Niektórzy nazywają to niedociągnięcie super(), i to jest w pewnym sensie, ale to też tylko niedociągnięcie w wielokrotnego dziedziczenia w ogóle. Diamentowe wzorce dziedziczenia są często podatne na błędy. Wiele z tych obejść prowadzi do jeszcze bardziej mylącego i podatnego na błędy kodu. Czasami najlepszą odpowiedzią jest próba naprawy kodu i użycie mniejszego dziedziczenia.