Investigamos en todo internet para darte la respuesta para tu dilema, si continúas con dudas déjanos la inquietud y contestaremos porque estamos para ayudarte.
Solución:
super()
le permite evitar referirse explícitamente a la clase base, lo cual puede ser bueno. Pero la principal ventaja viene con la herencia múltiple, donde pueden suceder todo tipo de cosas divertidas. Consulte los documentos estándar en super si aún no lo ha hecho.
Tenga en cuenta que la sintaxis cambió en Python 3.0: solo puede decir super().__init__()
en lugar de super(ChildB, self).__init__()
que en mi opinión es bastante mejor. Los documentos estándar también hacen referencia a una guía para usar super()
que es bastante explicativo.
estoy tratando de entender
super()
La razón por la que usamos super
es para que las clases secundarias que pueden estar usando herencia múltiple cooperativa llamarán a la siguiente función de clase principal correcta en el Orden de resolución de métodos (MRO).
En Python 3, podemos llamarlo así:
class ChildB(Base):
def __init__(self):
super().__init__()
En Python 2, estábamos obligados a usarlo así, pero evitaremos esto aquí:
super(ChildB, self).__init__()
Sin super, está limitado en su capacidad para usar herencia múltiple porque cablea la llamada del próximo padre:
Base.__init__(self) # Avoid this.
Explico más a continuación.
“¿Qué diferencia hay realmente en este código ?:”
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super().__init__()
La principal diferencia en este código es que en ChildB
obtienes una capa de indirección en el __init__
con super
, que utiliza la clase en la que se define para determinar la siguiente clase __init__
buscar en el MRO.
Ilustro esta diferencia en una respuesta a la pregunta canónica, ¿Cómo usar ‘super’ en Python ?, que demuestra inyección de dependencia y herencia múltiple cooperativa.
Si Python no tuviera super
Aquí hay un código que en realidad es muy equivalente a super
(cómo se implementa en C, menos algunas comprobaciones y comportamientos alternativos, y se traduce a Python):
class ChildB(Base):
def __init__(self):
mro = type(self).mro()
check_next = mro.index(ChildB) + 1 # next after *this* class.
while check_next < len(mro):
next_class = mro[check_next]
if '__init__' in next_class.__dict__:
next_class.__init__(self)
break
check_next += 1
Escrito un poco más como Python nativo:
class ChildB(Base):
def __init__(self):
mro = type(self).mro()
for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
if hasattr(next_class, '__init__'):
next_class.__init__(self)
break
Si no tuviéramos el super
objeto, tendríamos que escribir este código manual en todas partes (¡o recrearlo!) para asegurarnos de que llamamos al siguiente método adecuado en el orden de resolución de métodos.
¿Cómo super hace esto en Python 3 sin que se le diga explícitamente desde qué clase e instancia del método se llamó?
Obtiene el marco de pila de llamada y encuentra la clase (almacenada implícitamente como una variable local libre, __class__
, haciendo que la función de llamada sea un cierre sobre la clase) y el primer argumento de esa función, que debe ser la instancia o clase que le informa qué Orden de resolución de método (MRO) usar.
Dado que requiere ese primer argumento para el MRO, el uso de super
con static métodos es imposible ya que no tienen acceso al MRO de la clase desde la que se llaman.
Críticas a otras respuestas:
super () le permite evitar referirse explícitamente a la clase base, lo cual puede ser bueno. . Pero la principal ventaja viene con la herencia múltiple, donde pueden suceder todo tipo de cosas divertidas. Consulte los documentos estándar en super si aún no lo ha hecho.
Es bastante ondulante y no nos dice mucho, pero el punto de super
no es para evitar escribir la clase de padres. El punto es asegurarse de que se llame al siguiente método en línea en el orden de resolución del método (MRO). Esto se vuelve importante en la herencia múltiple.
Te lo explicaré aquí.
class Base(object):
def __init__(self):
print("Base init'ed")
class ChildA(Base):
def __init__(self):
print("ChildA init'ed")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("ChildB init'ed")
super().__init__()
Y creemos una dependencia que queremos que se llame después del Niño:
class UserDependency(Base):
def __init__(self):
print("UserDependency init'ed")
super().__init__()
Ahora recuerda ChildB
usa super, ChildA
no:
class UserA(ChildA, UserDependency):
def __init__(self):
print("UserA init'ed")
super().__init__()
class UserB(ChildB, UserDependency):
def __init__(self):
print("UserB init'ed")
super().__init__()
Y UserA
no llama al método UserDependency:
>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>
Pero UserB
de hecho llama a UserDependency porque ChildB
invoca super
:
>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>
Crítica por otra respuesta
En ninguna circunstancia debe hacer lo siguiente, que sugiere otra respuesta, ya que definitivamente obtendrá errores cuando subclase ChildB:
super(self.__class__, self).__init__() # DON'T DO THIS! EVER.
(Esa respuesta no es inteligente ni particularmente interesante, pero a pesar de las críticas directas en los comentarios y más de 17 votos en contra, el que respondió persistió en sugerirla hasta que un amable editor solucionó su problema).
Explicación: Usando self.__class__
como sustituto del nombre de la clase en super()
conducirá a la recursividad. super
Permítanos buscar el siguiente padre en el MRO (consulte la primera sección de esta respuesta) para las clases secundarias. Si tu dices super
estamos en el método de la instancia secundaria, luego buscará el siguiente método en línea (probablemente este) dando como resultado una recursividad, probablemente causando una falla lógica (en el ejemplo del respondedor, lo hace) o un RuntimeError
cuando se excede la profundidad de recursividad.
>>> class Polygon(object):
... def __init__(self, id):
... self.id = id
...
>>> class Rectangle(Polygon):
... def __init__(self, id, width, height):
... super(self.__class__, self).__init__(id)
... self.shape = (width, height)
...
>>> class Square(Rectangle):
... pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
File "", line 1, in
File "", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'
Python 3 es nuevo super()
El método de llamada sin argumentos afortunadamente nos permite eludir este problema.
Se ha observado que en Python 3.0+ puede usar
super().__init__()
para realizar su llamada, que es concisa y no requiere que haga referencia explícitamente a los nombres de clase O padres, lo que puede ser útil. Solo quiero agregar que para Python 2.7 o menos, algunas personas implementan un comportamiento insensible al nombre escribiendo self.__class__
en lugar del nombre de la clase, es decir
super(self.__class__, self).__init__() # DON'T DO THIS!
SIN EMBARGO, esto rompe las llamadas a super
para cualquier clase que herede de su clase, donde self.__class__
podría devolver una clase secundaria. Por ejemplo:
class Polygon(object):
def __init__(self, id):
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id)
self.shape = (width, height)
class Square(Rectangle):
pass
Aqui tengo una clase Square
, que es una subclase de Rectangle
. Digamos que no quiero escribir un constructor separado para Square
porque el constructor de Rectangle
es lo suficientemente bueno, pero por alguna razón quiero implementar un Square para poder volver a implementar algún otro método.
Cuando creo un Square
utilizando mSquare = Square('a', 10,10)
, Python llama al constructor para Rectangle
porque no he dado Square
su propio constructor. Sin embargo, en el constructor de Rectangle
, la llamada super(self.__class__,self)
va a devolver la superclase de mSquare
, por lo que llama al constructor para Rectangle
de nuevo. Así es como ocurre el bucle infinito, como lo mencionó @S_C. En este caso, cuando corro super(...).__init__()
Estoy llamando al constructor para Rectangle
pero como no le doy argumentos, obtendré un error.
Sección de Reseñas y Valoraciones
Finalizando este artículo puedes encontrar los comentarios de otros usuarios, tú además eres capaz insertar el tuyo si te apetece.