Saltar al contenido

Entendiendo Python super () con los métodos __init __ ()

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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *