2012-01-18 25 views
14

Al derivar de un tipo incorporado así como de alguna otra clase, parece que el constructor del tipo incorporado no llama al constructor de la superclase. Esto da como resultado que los métodos __init__ no se invoquen para los tipos que vienen después del built-in en el MRO.Python 3 tipos incorporados __init__ no llama a super() .__ init__?

Ejemplo:

class A: 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 
     print("A().__init__()") 

class B(list, A): 
    def __init__(self, *args, **kwargs): 
     print("B().__init__() start") 
     super().__init__(*args, **kwargs) 
     print("B().__init__() end") 

if __name__ == '__main__': 
    b = B() 

En esta muestra, A init__ .__ nunca es llamado. Cuando B se define como class B(A, list) en su lugar, cambiando el orden de herencia, funciona según lo previsto (es decir, se llama a .__ init__).

Esta muy sutil dependencia del orden de herencia parece bastante antiponética, ¿se pretende de esta manera? También significa que nunca debe derivarse de tipos incorporados en jerarquías de clases complejas, porque no puede saber dónde termina el edificio en el MRO cuando alguien más deriva de sus clases (horror de mantenimiento). ¿Me estoy perdiendo de algo?

Información adicional: Python versión 3.1

+0

Python nunca ha tenido invocación automática de la superclase '__init__' métodos – Marcin

+2

¿Cómo sería llamar' Un .__ init__' con? 'list .__ init__' toma un argumento y arroja un error si obtiene más. Incluso si aceptara otros argumentos arbitrarios, cortaría el primero, por lo que lo que A veía dependería del orden de herencia. –

+0

Una pregunta similar: http: // stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance Con la interpretación de Guido de la respuesta a esa pregunta: http://python-history.blogspot.com/2010/06/method- resolution-order.html – aganders3

Respuesta

9

El uso correcto de super() es bastante sutil y requiere un cierto cuidado si los métodos de colaboración no tienen todos la misma firma. El patrón habitual para __init__() métodos es la siguiente:

class A(object): 
    def __init__(self, param_a, **kwargs): 
     self.param_a = param_a 
     super(A, self).__init__(**kwargs) 

class B(A): 
    def __init__(self, param_b, **kwargs): 
     self.param_b = param_b 
     super(B, self).__init__(**kwargs) 

class C(A): 
    def __init__(self, param_c, **kwargs): 
     self.param_c = param_c 
     super(C, self).__init__(**kwargs) 

class D(B, C): 
    def __init__(self, param_d, **kwargs): 
     self.param_d = param_d 
     super(D, self).__init__(**kwargs) 

d = D(param_a=1, param_b=2, param_c=3, param_d=4) 

Tenga en cuenta que esto requiere que todos los métodos colaboran, y que todos los métodos necesitan una firma poco compatibles para asegurar que no importa en ese momento el método se llama.

Los constructores de tipos incorporados no tienen firmas de constructor que permitan participar en dicha colaboración. Incluso si llamaran al super().__init__(), esto sería bastante inútil a menos que todas las firmas de los constructores estuvieran unificadas. Por lo tanto, al final tienes razón: no son adecuados para la participación en llamadas de constructores colaborativos.

super() solo se puede utilizar si todos los métodos colaborativos tienen la misma firma (como, por ejemplo, __setattr__()) o si utiliza el patrón anterior (o similar). Sin embargo, el uso de super() no es el único patrón para llamar a los métodos de clase base. Si no hay "diamantes" en su patrón de herencia múltiple, puede usar llamadas de clase base explícitas, por ejemplo B.__init__(self, param_a). Las clases con múltiples clases base simplemente llaman a múltiples constructores. Incluso si hay diamantes, a veces puede usar llamadas explícitas, siempre que tenga cuidado de llamar __init__() varias veces sin causar daños.

Si desea utilizar super() para contructors de todos modos, no debe usar subclases de tipos incorporados (excepto object) en varias jerarquías de herencia. Algunos lectura adicional:

Cuestiones relacionadas