2010-09-17 16 views
9

in primo luogo, vorrei citare un po 'un saggio da "esperti di programmazione Python" libro:miscelazione eccellenti e classici chiamate in Python

Nell'esempio seguente, una classe C che chiama le sue classi di base utilizzando il metodo __init__ Sarà fare in modo che la classe B venga chiamata due volte!

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 

e, infine, ecco una spiegazione di ciò sta succedendo qui:

Questo accade a causa della .__ init __ (self) chiamata A, che è fatta con l'istanza C, rendendo super (A, self) .__ init __() chiama il costruttore di B. In altre parole, super deve essere utilizzato nell'intera gerarchia di classi. Il problema è che a volte una parte di questa gerarchia si trova nel codice di terze parti.

Non ho idea del perché "super(A, self).__init__() chiama il costruttore di B". Per favore, spiega questo momento. Molte grazie.

risposta

4

La documentazione per super dice che:

restituire un oggetto proxy che il metodo delegati chiama ad un genitore o un fratello classe di tipo. Questo è utile per accedere ai metodi ereditati che sono stati sovrascritti in una classe. L'ordine di ricerca è uguale a quello utilizzato da getattr(), tranne per il fatto che il tipo stesso viene saltato.

Quando si esegue A.__init__(self) dall'interno C della super(A, self) tornerà <super: <class 'A'>, <C object>>. Poiché l'istanza è C (<C object>) vengono raccolte tutte le classi nella gerarchia di ereditarietà di C. E la chiamata __init__ viene emessa su tutti loro. Di conseguenza vedi "B" essere chiamato due volte.

Per verificare questo aggiungere un'altra classe 'Z' e lasciare 'C' ereditare da 'Z' pure. Guarda cosa succede.

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) 

In questo caso, A chiamerà B e Z. B chiamerà anche Z.

9

Per comprendere questo comportamento, è necessario comprendere che super non chiama la classe base, ma ricerca il successivo metodo di abbinamento lungo l'ordine nello __mro__. Quindi, la chiamata super(A, self).__init__() guarda allo __mro__ == ['C', 'A', 'B', 'object'], vede B come la classe successiva con un metodo di abbinamento e chiama il metodo (costruttore) di B.

Se si cambia C a

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

si ottiene

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

che mostra come il costruttore di A chiamate B.

+1

grazie. una domanda - è vero che quando "super (A, self) .__ init __()" viene richiamato all'interno del costruttore di classe A, il suo argomento "self" è uguale alla nostra istanza C che abbiamo appena istanziato? – varnie

+2

@varnie: sì. Puoi 'stampare super (A, self)' all'interno del metodo A __init __() 'per vedere che' self' è in effetti un'istanza 'C'. –

Problemi correlati