in

class – Comprensión de Python super () con métodos __init __ ()

apple touch icon@2

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, teníamos que llamar super así con el nombre de la clase definitoria y self, pero evitaremos esto de ahora en adelante porque es redundante, más lento (debido a las búsquedas de nombres) y más detallado (¡así que actualice su Python si aún no lo ha hecho!):

        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.

Ilustraré 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 llamar 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, utilizando super con métodos estáticos es imposible ya que no tienen acceso al MRO de la clase desde la que son llamados.

Críticas a otras respuestas:

super () le permite evitar referirse explícitamente a la clase base, lo que 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 "<stdin>", line 1, in <module>
  File "<stdin>", 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.

Deja una respuesta

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

validation testing

Pruebas de validación

Método Java Character toUpperCase () – Javatpoint