2012-01-23 8 views
52

Así que estaba siguiendo Python's Super Considered Harmful, y fui a probar sus ejemplos.manera correcta de usar super (argumento que pasa)

Sin embargo, Example 1-3, que se supone que muestra la forma correcta de llamar al manejar super__init__ métodos que esperan diferentes argumentos, de plano no funciona.

Esto es lo que me sale:

~ $ python example1-3.py 
MRO: ['E', 'C', 'A', 'D', 'B', 'object'] 
E arg= 10 
C arg= 10 
A 
D arg= 10 
B 
Traceback (most recent call last): 
    File "Download/example1-3.py", line 27, in <module> 
    E(arg=10) 
    File "Download/example1-3.py", line 24, in __init__ 
    super(E, self).__init__(arg, *args, **kwargs) 
    File "Download/example1-3.py", line 14, in __init__ 
    super(C, self).__init__(arg, *args, **kwargs) 
    File "Download/example1-3.py", line 4, in __init__ 
    super(A, self).__init__(*args, **kwargs) 
    File "Download/example1-3.py", line 19, in __init__ 
    super(D, self).__init__(arg, *args, **kwargs) 
    File "Download/example1-3.py", line 9, in __init__ 
    super(B, self).__init__(*args, **kwargs) 
TypeError: object.__init__() takes no parameters 

Parece que object sí viola una de las mejores prácticas mencionadas en el documento, que es que los métodos que utilizan super deben aceptar *args y **kwargs.

Ahora, obviamente, el Sr. Knight esperaba que sus ejemplos funcionaran, ¿es esto algo que se modificó en las versiones recientes de Python? Revisé 2.6 y 2.7, y falla en ambos.

Entonces, ¿cuál es la forma correcta de tratar este problema?

+2

Mi forma preferida es: jerarquías de herencia planas y simples. – millimoose

+14

También debería leer ["Super() de Python se considera super"] (http://rhettinger.wordpress.com/2011/05/26/super-considered-super/) para obtener una vista equilibrada :) –

+0

@ BjörnPollex: ¡Gracias! Su enlace proporciona una respuesta a la pregunta: puede escribir una "clase de raíz" que hereda de 'objeto', y se asegura de llamar correctamente a' objeto'' __init__'. – cha0site

Respuesta

55

A veces dos clases pueden tener algunos nombres de parámetros en común. En ese caso, no puede sacar los pares clave-valor de **kwargs o eliminarlos del *args. En su lugar, se puede definir una clase Base que a diferencia de object, absorbe/ignora argumentos:

class Base(object): 
    def __init__(self, *args, **kwargs): pass 

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

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

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

class D(B): 
    def __init__(self, arg, *args, **kwargs): 
     print "D", "arg=",arg 
     super(D, self).__init__(arg, *args, **kwargs) 

class E(C,D): 
    def __init__(self, arg, *args, **kwargs): 
     print "E", "arg=",arg 
     super(E, self).__init__(arg, *args, **kwargs) 

print "MRO:", [x.__name__ for x in E.__mro__] 
E(10) 

produce

MRO: ['E', 'C', 'A', 'D', 'B', 'Base', 'object'] 
E arg= 10 
C arg= 10 
A 
D arg= 10 
B 

Tenga en cuenta que para que esto funcione, debe haber Base la penúltima clase en el MRO.

+2

No debería 'Base' llamar' super (Base, self) .__ init __() '? – cha0site

+2

Para que esto funcione, 'Base' debe venir al final de MRO (excepto' object'). Llamar 'object .__ init__' no hace nada, por lo que está bien no llamar a' super (Base, self) .__ init __() '. De hecho, creo que puede ser más claro no incluir 'super (Base, self) .__ init __()' para llevar a casa el punto de que 'Base' es el final de la línea. – unutbu

5

Como se explicó en Python's super() considered super, una forma es hacer que la clase coma los argumentos que requiere, y pasar el resto. Por lo tanto, cuando la cadena de llamadas alcanza el object, se han consumido todos los argumentos y se llamará object.__init__ sin argumentos (como se espera). Así que el código debería tener este aspecto:

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

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

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

class D(B): 
    def __init__(self, arg, *args, **kwargs): 
     print "D", "arg=",arg 
     super(D, self).__init__(*args, **kwargs) 

class E(C,D): 
    def __init__(self, arg, *args, **kwargs): 
     print "E", "arg=",arg 
     super(E, self).__init__(*args, **kwargs) 

print "MRO:", [x.__name__ for x in E.__mro__] 
E(10, 20, 30) 
+0

Entonces, ¿qué pasa con 'E (arg = 10)'? No me gusta este método, necesito conocer el MRO por adelantado para poder usarlo. El otro método donde escribo una "clase de raíz" que hereda de 'objeto' parece mucho más limpio. – cha0site

+0

Este método es útil si las funciones llamadas no comparten nombres de argumento. Admito que no tengo mucha experiencia práctica con 'super', por lo que este enfoque podría ser bastante teórico. –

14

Si usted va a tener un montón de sucesiones (que es el caso aquí) Sugiero que usted pueda pasar todos los parámetros utilizando **kwargs, y luego pop ellos justo después de utilizar ellos (a menos que los necesites en las clases altas).

class First(object): 
    def __init__(self, *args, **kwargs): 
     self.first_arg = kwargs.pop('first_arg') 
     super(First, self).__init__(*args, **kwargs) 

class Second(First): 
    def __init__(self, *args, **kwargs): 
     self.second_arg = kwargs.pop('second_arg') 
     super(Second, self).__init__(*args, **kwargs) 

class Third(Second): 
    def __init__(self, *args, **kwargs): 
     self.third_arg = kwargs.pop('third_arg') 
     super(Third, self).__init__(*args, **kwargs) 

Esta es la forma más sencilla de resolver ese tipo de problemas.

third = Third(first_arg=1, second_arg=2, third_arg=3) 
Cuestiones relacionadas