2009-09-15 17 views
6

Ho visto alcune domande su questo argomento, ma non sono stato in grado di trovare una risposta definitiva.Come si mischiano le classi Python vecchio stile e nuovo stile?

Mi piacerebbe conoscere il modo corretto di utilizzare le classi vecchio stile in un nuovo codice in Python. Diciamo ad esempio che ho due classi fisse, A e B. Se voglio sottoclasse A e B, e convertire in classi di nuovo stile (A2 e B2), questo funziona. Tuttavia c'è un problema se voglio creare una nuova classe C, da A2 e B2.

Pertanto, è possibile continuare con questo metodo o tutte le classi devono essere conformi al vecchio stile se una qualsiasi classe di base è definita come vecchio stile?

Vedere il codice di esempio per chiarire:

class A: 
    def __init__(self): 
     print 'class A' 

class B: 
    def __init__(self): 
     print 'class B' 

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

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

class C(A2, B2): 
    def __init__(self): 
     super(C,self).__init__() 
     print 'class C' 

A2() 
print '---' 
B2() 
print '---' 
C() 

L'output di questo codice:

class A 
class A2 
--- 
class B 
class B2 
--- 
class A 
class A2 
class C 

Come si può vedere, il problema è che nella chiamata a C(), classe B2 non è mai stato inizializzato.


Update - New-Style Class Esempio

credo che non è chiaro quale sia la sequenza di inizializzazione corretta dovrebbe essere quando si utilizza super. Ecco un esempio funzionante in cui una chiamata a super fa inizializza tutte le classi base, non solo la prima trova.

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

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

class A2(A): 
    def __init__(self): 
     super(A2, self).__init__() 
     print 'class A2' 

class B2(B): 
    def __init__(self): 
     super(B2, self).__init__() 
     print 'class B2' 

class C(A2, B2): 
    def __init__(self): 
     super(C, self).__init__() 
     print 'class C' 

C() 

e produce l'uscita:

class B 
class B2 
class A 
class A2 
class C 
+0

Risposta aggiornata per includere la spiegazione dell'ereditarietà del diamante e il problema risolto con le classi di nuovo stile. –

+0

Perché hai addirittura classi vecchio stile in primo luogo? –

+0

Moduli di terze parti, forse? –

risposta

6

Questa non è una questione di mescolare classi vecchie e nuove di stile. super() non chiama tutte le funzioni delle classi base, chiama il primo che trova secondo l'ordine di risoluzione dei metodi. In questo caso A2, che a sua volta chiama A.

Se si desidera chiamare sia, farlo in modo esplicito:

class C(A2, B2): 
    def __init__(self): 
     A2.__init__(self) 
     B2.__init__(self) 
     print 'class C' 

Questo dovrebbe risolverlo.

Aggiornamento:

Il problema diamante eredità, come si fa riferimento, è la questione di quale classe di chiamare in una situazione di diamante eredità, in questo modo:

class A: 
    def method1(self): 
     print 'class A' 

    def method2(self): 
     print 'class A' 

class B(A): 
    def method1(self): 
     print 'class B' 

class C(A): 
    def method1(self): 
     print 'class C' 

    def method2(self): 
     print 'class C' 

class D(B, C): 
    pass 

testare questo fuori:

>>> D().method1() 
'class B' 

Questo è corretto. Chiama l'implementazione della prima classe. Tuttavia, proviamo questo con method2:

>>> D().method2() 
'class A' 

Oups, SBAGLIATO! Avrebbe dovuto chiamare class C.method2() qui, perché anche se la classe B non sovrascrive il metodo 2, la classe C. Ora fanno classe A una classe newstyle:

class A(object): 
    def method1(self): 
     print 'class A' 

e riprova:

>>> D().method1() 
'class B' 
>>> D().method2() 
'class C' 

e oplà, funziona. Questa è la differenza di ordine della risoluzione dei metodi tra classi nuove e vecchie, e questo è ciò che a volte rende confuso il loro mixaggio.

Notate come mai B e C vengono chiamati. Questo è vero anche se chiamiamo super.

Se si desidera chiamare sia B che C, è necessario chiamare entrambi in modo esplicito.

Ora, se si Unbreak il diamante, come nel tuo esempio avere classi di base separati, il risultato è diverso:

class A1(object): 
    def method1(self): 
     print 'class A1' 

    def method2(self): 
     print 'class A1' 

class A2(object): 
    def method1(self): 
     print 'class A2' 

    def method2(self): 
     print 'class A2' 

class B(A1): 
    def method1(self): 
     print 'class B' 

class C(A2): 
    def method1(self): 
     print 'class C' 

    def method2(self): 
     print 'class C' 

class D(B, C): 
    def method1(self): 
     super(D, self).method1() 

    def method2(self): 
     super(D, self).method2() 


>>> D().method1() 
'class B' 
>>> D().method2() 
'class A1' 

Questo è anche per il design. Ancora da nessuna parte vengono chiamate due classi base. Se vuoi che ciò accada, devi comunque chiamare entrambi in modo esplicito.

+0

Super chiamerà tutti i metodi della classe base se implementato con tutte le classi di nuovo stile e utilizzato in modo coerente. La mia domanda è come gestire il caso quando si è costretti a mescolare in una classe vecchio stile. Sono a conoscenza di come chiamare direttamente i metodi padre, ma questo si interrompe per l'ereditarietà dei diamanti. – cmcginty

+0

No, non lo farò. Se crei le classi di nuovo stile A e B, ottieni esattamente lo stesso risultato di cui sopra. E non vedo come romperebbe l'eredità del diamante per chiamare direttamente la chiamata i metodi genitore. Ho il sospetto che tu abbia il problema dell'ereditarietà del diamante leggermente all'indietro. In effetti, sembra che l'ereditarietà del diamante debba chiamare entrambe le classi base (che in genere non lo è) e l'unico modo per farlo in Python è chiamarle esplicitamente. –

+0

Vedere il mio aggiornamento, esegue super tutti i metodi \ __ init \ __. – cmcginty

Problemi correlati