2016-01-19 31 views
14

Si consideri il seguente frammento di codice pythonpitone più argomenti eredità passaggio ai costruttori che utilizzano 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 

Mi chiedo come posso passare a, b e c ai costruttori classi di base corrispondenti.

Grazie,

+5

Possibile duplicato di [Come funziona Python super() con ereditarietà multipla?] (Http://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple- eternity) – erip

+0

tu può chiamare '__init__' due volte per' B' e 'C'. – sobolevn

+0

Ma allora "A .__ init__" verrebbe chiamato due volte, vero? – user5459381

risposta

22

Bene, quando si tratta di ereditarietà multipla in generale, le classi di base (sfortunatamente) devono essere progettate per ereditarietà multipla. Le classi B e C nell'esempio non lo sono e pertanto non è possibile trovare un modo corretto per applicare super in D.

Uno dei modi più comuni di progettare il vostro classi base per l'ereditarietà multipla, è per le classi di base a livello centrale per accettare args extra in loro metodo __init__, che non si intende utilizzare, e passare lungo il loro super chiamata.

Ecco un modo per farlo in python:

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 

Questo può essere visto come deludente, ma questo è solo il modo in cui è.

+1

Non devi scrivere 'kw.pop ('qualcosa')', puoi semplicemente prendere 'qualcosa' come argomento. – GingerPlusPlus

+0

@GingerPlusPlus giusto! risolto, grazie. (Ho iniziato a scriverlo pensando a position, poi sono passato a kwargs ...) – shx2

+1

Grazie, shx2, bella soluzione! – Ling

3

Purtroppo, non c'è modo di fare questo lavoro utilizzando super() senza modificare le classi di base. Qualsiasi chiamata ai costruttori per B o C sta andando a cercare e chiamare la classe successiva nella Method Resolution Order, che sarà sempre B o C anziché la classe A che i costruttori della classe B e C assumono.

L'alternativa è chiamare i costruttori esplicitamente senza l'uso di super() in ogni classe.

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 

C'è ancora un aspetto negativo qui come il costruttore A sarebbe chiamato due volte, che in realtà non hanno molto di un effetto in questo esempio, ma può causare problemi nei costruttori più complesse. È possibile includere un controllo per impedire il funzionamento del costruttore più di una volta.

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

Qualcuno potrebbe chiamare questa una lacuna di super(), ed è in un certo senso, ma è anche solo una lacuna ereditarietà multipla in generale. I modelli di ereditarietà dei diamanti sono spesso soggetti a errori. E un sacco di soluzioni alternative per loro portano a un codice ancora più confuso e soggetto a errori. A volte, la risposta migliore è provare e rifattorizzare il codice per utilizzare meno eredità multiple.