2010-09-17 9 views
9

primer lugar, vamos a citar un poco un ensayo del libro "Programación Avanzado Python":mezcla súper clásicos y llamadas en Python

En el siguiente ejemplo, una clase C que llama a sus clases base utilizando el método __init__ ¡ hará que la clase B se llame dos veces!

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 

y, por último, aquí es una explicación de lo que está pasando aquí:

Esto sucede debido a la __ (self) llamada A .__ init, que se hace con la instancia C, haciendo de esta manera que el constructor de B de super (A, self) .__ init __(). En otras palabras, super debe usarse en toda la jerarquía de clases. El problema es que a veces parte de esta jerarquía se encuentra en código de terceros.

no tengo idea de por qué "super(A, self).__init__() llama al constructor de B". Por favor explique este momento. Muchas gracias.

Respuesta

4

La documentación para super dice que:

devolver un objeto proxy que los delegados método llama a una clase de padres o hermanos de tipo. Esto es útil para acceder a métodos heredados que se han reemplazado en una clase. El orden de búsqueda es el mismo que el usado por getattr(), excepto que el tipo en sí se omite.

Cuando se ejecuta desde dentro A.__init__(self)C la super(A, self) volverá <super: <class 'A'>, <C object>>. Como la instancia es C (<C object>), se seleccionan todas las clases en la jerarquía de herencia de C. Y la llamada __init__ se emite en todos ellos. En consecuencia, ves 'B' siendo llamado dos veces.

Para verificar esto agregue otra clase 'Z' y deje que 'C' herede de 'Z' también. Mira qué pasa.

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) 

En este caso, A llamarán B y Z. B llamará al Z también.

9

Para comprender este comportamiento, debe comprender que super no llama a la clase base, sino que busca el siguiente método de coincidencia a lo largo del orden en el __mro__. Por lo tanto, la llamada super(A, self).__init__() mira __mro__ == ['C', 'A', 'B', 'object'], ve B como la siguiente clase con un método coincidente y llama al método (constructor) de B.

Si cambia C a

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

se obtiene

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

que muestra cómo el constructor de llamadas AB.

+1

gracias. una pregunta: ¿es cierto que cuando se invoca a "super (A, self) .__ init __()" dentro del constructor de clase A, su argumento "self" es igual a nuestra instancia C que acabamos de instanciar? – varnie

+2

@varnie: sí. Puede 'imprimir super (A, self)' dentro del método '__init __()' de A para ver que 'self' es de hecho una instancia' C'. –