Cuál es la diferencia entre:
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
y:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
He visto que super
se usa bastante en clases con una sola herencia. Puedo ver por qué lo usarías en herencia múltiple, pero no tengo claro cuáles son las ventajas de usarlo en este tipo de situación.
python
oop
inheritance
super
usuario25785
fuente
fuente
¿Cual es la diferencia?
significa llamar
SomeBaseClass
's__init__
. mientrassignifica llamar a un enlace
__init__
de la clase padre que sigueChild
en la Orden de resolución de método (MRO) de la instancia.Si la instancia es una subclase de Child, puede haber un padre diferente que viene después en el MRO.
Explicado simplemente
Cuando escribe una clase, desea que otras clases puedan usarla.
super()
facilita que otras clases usen la clase que estás escribiendo.Como dice Bob Martin, una buena arquitectura le permite posponer la toma de decisiones el mayor tiempo posible.
super()
puede habilitar ese tipo de arquitectura.Cuando otra clase subclasifica la clase que escribió, también podría heredar de otras clases. Y esas clases podrían tener una
__init__
que viene después de esto en__init__
función del orden de las clases para la resolución del método.Sin
super
usted, probablemente codificaría al padre de la clase que está escribiendo (como lo hace el ejemplo). Esto significaría que no llamaría al siguiente__init__
en el MRO y, por lo tanto, no podría reutilizar el código en él.Si está escribiendo su propio código para uso personal, es posible que no le importe esta distinción. Pero si desea que otros usen su código, usar
super
es una cosa que permite una mayor flexibilidad para los usuarios del código.Python 2 contra 3
Esto funciona en Python 2 y 3:
Esto solo funciona en Python 3:
Funciona sin argumentos subiendo en el marco de la pila y obteniendo el primer argumento para el método (generalmente
self
para un método de instancia ocls
para un método de clase, pero podrían ser otros nombres) y encontrar la clase (por ejemploChild
) en las variables libres ( se busca con el nombre__class__
como una variable de cierre libre en el método).Prefiero demostrar la forma compatible de usar
super
, pero si solo usas Python 3, puedes llamarlo sin argumentos.Indirección con compatibilidad hacia adelante
¿Qué te da? Para la herencia única, los ejemplos de la pregunta son prácticamente idénticos desde el punto de vista del análisis estático. Sin embargo, el uso
super
le brinda una capa de indirección con compatibilidad hacia adelante.La compatibilidad directa es muy importante para los desarrolladores experimentados. Desea que su código siga funcionando con cambios mínimos a medida que lo cambia. Cuando mira su historial de revisiones, desea ver con precisión qué cambió cuándo.
Puede comenzar con una sola herencia, pero si decide agregar otra clase base, solo tiene que cambiar la línea con las bases: si las bases cambian en una clase de la que hereda (digamos que se agrega un mixin), cambiaría Nada en esta clase. Particularmente en Python 2, obtener los argumentos
super
y los argumentos correctos del método puede ser difícil. Si sabe que lo está utilizandosuper
correctamente con herencia única, eso hace que la depuración sea menos difícil en el futuro.Inyección de dependencia
Otras personas pueden usar su código e inyectar a los padres en la resolución del método:
Supongamos que agrega otra clase a su objeto y desea inyectar una clase entre Foo y Bar (para probar o por algún otro motivo):
El uso de un super-niño no puede inyectar la dependencia porque el niño que está usando ha codificado el método que se llamará después del suyo:
Sin embargo, la clase con el hijo que usa
super
puede inyectar correctamente la dependencia:Dirigiendo un comentario
Python linealiza un árbol de herencia complicado a través del algoritmo de linealización C3 para crear un Orden de resolución de método (MRO).
Queremos buscar métodos en ese orden .
Para que un método definido en un padre encuentre el siguiente en ese orden sin
super
él, tendría queEl
UnsuperChild
qué no tienen acceso aInjectMe
. Es alUnsuperInjector
que tiene accesoInjectMe
, y sin embargo no puede llamar al método de esa clase desde el método del que heredaUnsuperChild
.Ambas clases secundarias tienen la intención de llamar a un método con el mismo nombre que viene después en el MRO, que podría ser otra clase que no conocía cuando se creó.
El que no tiene
super
códigos rígidos es el método de su padre, por lo tanto, ha restringido el comportamiento de su método y las subclases no pueden inyectar funcionalidad en la cadena de llamadas.El uno con
super
tiene mayor flexibilidad. La cadena de llamadas para los métodos puede ser interceptada e inyectada funcionalidad.Es posible que no necesite esa funcionalidad, pero los subclases de su código sí.
Conclusión
Siempre use
super
para hacer referencia a la clase padre en lugar de codificarla.Lo que pretende es hacer referencia a la clase principal que sigue a la línea, no específicamente a la que ve heredar el elemento secundario.
No usar
super
puede imponer restricciones innecesarias a los usuarios de su código.fuente
list
interfaz, digamos quedoublylinkedlist
la aplicación la selecciona sin problemas. Puedo hacer que mi ejemplo sea más configurable mediante la introducciónconfig.txt
y la implementación de enlaces en el momento de la carga. ¿Es este el ejemplo correcto? En caso afirmativo, ¿cómo relaciono su código? Vea el primer aviso de DI en wiki. ¿Dónde se puede configurar una nueva implementación? en su códigoInjectMe
clase. Sin embargo, los comentarios no son para discusión, por lo que le sugiero que discuta esto más a fondo en el chat o haga una nueva pregunta en el sitio principal.__init__
funciones. especialmente si la firma de__init__
varía entre clases en la jerarquía. He agregado una respuesta que se centra en este aspectoJugué un poco
super()
y reconocí que podemos cambiar el orden de las llamadas.Por ejemplo, tenemos la siguiente estructura de jerarquía:
En este caso, MRO de D será (solo para Python 3):
Creemos una clase donde
super()
llame después de la ejecución del método.Entonces podemos ver que el orden de resolución es el mismo que en MRO. Pero cuando llamamos
super()
al comienzo del método:Tenemos un orden diferente, se invierte un orden de la tupla MRO.
Para lecturas adicionales, recomendaría las siguientes respuestas:
fuente
¿No supone todo esto que la clase base es una clase de estilo nuevo?
No funcionará en Python 2.
class A
debe ser de nuevo estilo, es decir:class A(object)
fuente
Al llamar
super()
para resolver la versión de un método de clase, método de instancia o método estático de un padre, queremos pasar la clase actual en cuyo alcance estamos como primer argumento, para indicar a qué alcance del padre estamos tratando de resolver, y como un segundo argumento el objeto de interés para indicar a qué objeto estamos tratando de aplicar ese alcanceConsidere una jerarquía de clases
A
,B
yC
donde cada clase es el padre de la que le sigue, ya
,b
yc
instancias respectivas de cada uno.Usar
super
con un método estáticopor ejemplo, usar
super()
desde dentro del__new__()
métodoExplicación:
1- pesar de que es habitual para
__new__()
tomar como primer parámetro una referencia a la clase de llamada, se no implementado en Python como classmethod, sino más bien un métodoestático. Es decir, una referencia a una clase debe pasarse explícitamente como primer argumento cuando se llama__new__()
directamente:2- cuando
super()
llamamos para llegar a la clase principal, pasamos la clase secundariaA
como primer argumento, luego pasamos una referencia al objeto de interés, en este caso, es la referencia de clase que se pasó cuandoA.__new__(cls)
se llamó. En la mayoría de los casos, también es una referencia a la clase secundaria. En algunas situaciones, podría no serlo, por ejemplo, en el caso de herencias de múltiples generaciones.3- dado que, como regla general,
__new__()
es un método estático,super(A, cls).__new__
también devolverá un método estático y debe proporcionarse todos los argumentos explícitamente, incluida la referencia al objeto de interés, en este casocls
.4- haciendo lo mismo sin
super
Usar
super
con un método de instanciapor ejemplo, usar
super()
desde dentro__init__()
Explicación:
1-
__init__
es un método de instancia, lo que significa que toma como primer argumento una referencia a una instancia. Cuando se llama directamente desde la instancia, la referencia se pasa implícitamente, es decir, no necesita especificarla:2- al llamar
super()
dentro__init__()
, pasamos la clase secundaria como primer argumento y el objeto de interés como segundo argumento, que en general es una referencia a una instancia de la clase secundaria.3- La llamada
super(A, self)
devuelve un proxy que resolverá el alcance y lo aplicaráself
como si ahora fuera una instancia de la clase principal. Llamemos a ese proxys
. Dado que__init__()
es un método de instancia, la llamadas.__init__(...)
pasará implícitamente una referencia deself
como el primer argumento al padre__init__()
.4- hacer lo mismo sin que
super
tengamos que pasar una referencia a una instancia explícitamente a la versión principal de__init__()
.Usar
super
con un método de claseExplicación:
1- Se puede llamar a un método de clase directamente desde la clase y toma como primer parámetro una referencia a la clase.
2- cuando se llama
super()
dentro de un método de clase para resolver su versión principal, queremos pasar la clase secundaria actual como primer argumento para indicar a qué alcance primario estamos tratando de resolver, y el objeto de interés como segundo argumento para indicar a qué objeto queremos aplicar ese alcance, que en general es una referencia a la clase secundaria en sí o a una de sus subclases.3- La llamada se
super(B, cls)
resuelve al alcanceA
y se aplica a ellacls
. Comoalternate_constructor()
es un método de clase, la llamadasuper(B, cls).alternate_constructor(...)
pasará implícitamente una referencia decls
como primer argumento aA
la versión dealternate_constructor()
4- para hacer lo mismo sin usar
super()
, necesitaría obtener una referencia a la versión independiente deA.alternate_constructor()
(es decir, la versión explícita de la función). Simplemente hacer esto no funcionaría:Lo anterior no funcionaría porque el
A.alternate_constructor()
método toma una referencia implícitaA
como su primer argumento. Elcls
ser pasado aquí sería su segundo argumento.fuente
Muchas respuestas geniales, pero para estudiantes visuales: primero exploremos con argumentos para super, y luego sin.
Imagine que hay una instancia
jack
creada a partir de la claseJack
, que tiene la cadena de herencia como se muestra en verde en la imagen. Vocación:super(Jack, jack).method(...)
utilizará el MRO (Orden de resolución de método) de
jack
(su árbol de herencia en un cierto orden) y comenzará a buscar desdeJack
. ¿Por qué se puede proporcionar una clase para padres? Bueno, si comenzamos a buscar desde la instanciajack
, encontraría el método de la instancia, el punto es encontrar el método de sus padres.Si uno no proporciona argumentos a super, es como el primer argumento pasado es la clase de
self
, y el segundo argumento pasado esself
. Estos se calculan automáticamente en Python3.Sin embargo, digamos que no queremos usar
Jack
el método, en lugar de pasarJack
, podríamos pasarJen
para comenzar a buscar el método desde arribaJen
.Busca una capa a la vez (ancho no profundidad), por ejemplo, si
Adam
ySue
ambos tienen el método requerido,Sue
se encontrará primero el de.Si
Cain
ySue
ambos tuvieran el método requerido,Cain
primero se llamaría al método. Esto corresponde en código a:MRO es de izquierda a derecha.
fuente
Aquí hay algunas respuestas excelentes, pero no abordan cómo usarlas
super()
en el caso de que diferentes clases en la jerarquía tengan firmas diferentes ... especialmente en el caso de__init__
para responder esa parte y poder usarla de manera efectiva
super()
, sugeriría leer mi respuesta super () y cambiar la firma de los métodos cooperativos .Aquí está la solución a este escenario:
ejemplo de uso:
salida:
fuente
Esto es bastante fácil de entender.
Ok, qué pasa ahora si usas
super(Child,self)
?Cuando se crea una instancia de Child, su MRO (Orden de resolución de método) está en el orden de (Child, SomeBaseClass, object) en función de la herencia. (suponga que SomeBaseClass no tiene otros padres excepto el objeto predeterminado)
Al pasar
Child, self
,super
busca en el MRO de laself
instancia y devuelve el objeto proxy al lado de Child, en este caso es SomeBaseClass, este objeto invoca el__init__
método de SomeBaseClass. En otras palabras, si essuper(SomeBaseClass,self)
, el objeto proxy quesuper
devuelve seríaobject
Para la herencia múltiple, el MRO podría contener muchas clases, por lo que básicamente le
super
permite decidir dónde desea comenzar a buscar en el MRO.fuente
Considere el siguiente código:
Si cambia el constructor de
Y
aX.__init__
, obtendrá:Pero usando
super(Y, self).__init__()
, obtendrás:Y
P
oQ
incluso puede estar involucrado desde otro archivo que no sabe cuando escribeX
yY
. Entonces, básicamente, no sabrá a qué sesuper(Child, self)
referirá cuando esté escribiendoclass Y(X)
, incluso la firma de Y es tan simple comoY(X)
. Es por eso que Super podría ser una mejor opción.fuente