Posteriormente a consultar expertos en el tema, programadores de diversas ramas y profesores hemos dado con la respuesta a la cuestión y la dejamos plasmada en esta publicación.
Solución:
Esto se detalla con una cantidad razonable de detalles por parte del propio Guido en su publicación de blog Method Resolution Order (incluidos dos intentos anteriores).
En tu ejemplo, Third()
llamará First.__init__
. Python busca cada attribute en los padres de la clase como se enumeran de izquierda a derecha. En este caso, estamos buscando __init__
. Entonces, si defines
class Third(First, Second):
...
Python comenzará mirando First
y si First
no tiene el attributeentonces mirará Second
.
Esta situación se vuelve más compleja cuando la herencia comienza a cruzarse (por ejemplo, si First
heredado de Second
). Lea el enlace anterior para obtener más detalles, pero, en pocas palabras, Python intentará mantener el orden en que aparece cada clase en la lista de herencia, comenzando con la clase secundaria en sí.
Entonces, por ejemplo, si tuvieras:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First):
def __init__(self):
print "third"
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"
el MRO sería [Fourth, Second, Third, First].
Por cierto: si Python no puede encontrar un orden de resolución de método coherente, generará una excepción, en lugar de recurrir al comportamiento que podría sorprender al usuario.
Editado para agregar un ejemplo de un MRO ambiguo:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
print "third"
Deberían Third
‘s MRO ser [First, Second]
o [Second, First]
? No hay una expectativa obvia, y Python generará un error:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
Editar: Veo a varias personas argumentando que los ejemplos anteriores carecen super()
llamadas, déjame explicarte: el objetivo de los ejemplos es mostrar cómo se construye el MRO. Ellos son no destinado a imprimir “primeronsegundotercero” o lo que sea. Puede, y debe, por supuesto, jugar con el ejemplo, agregar super()
llamadas, vea lo que sucede y obtenga una comprensión más profunda del modelo de herencia de Python. Pero mi objetivo aquí es mantenerlo simple y mostrar cómo se construye el MRO. Y se construye como expliqué:
>>> Fourth.__mro__
(,
, ,
,
)
Su código y las otras respuestas tienen errores. les falta el super()
llamadas en las dos primeras clases que se requieren para que funcione la subclase cooperativa.
Aquí hay una versión fija del código:
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
los super()
call encuentra el siguiente método en el MRO en cada paso, razón por la cual First y Second también deben tenerlo; de lo contrario, la ejecución se detiene al final de Second.__init__()
.
Esto es lo que obtengo:
>>> Third()
second
first
third
Quería elaborar un poco la respuesta sin vida porque cuando comencé a leer sobre cómo usar super() en una jerarquía de herencia múltiple en Python, no lo entendí de inmediato.
Lo que tienes que entender es que super(MyClass, self).__init__()
proporciona el próximo__init__
método según el algoritmo Method Resolution Ordering (MRO) utilizado en el contexto de la jerarquía de herencia completa.
Esta última parte es crucial para entender. Consideremos el ejemplo de nuevo:
#!/usr/bin/env python2
class First(object):
def __init__(self):
print "First(): entering"
super(First, self).__init__()
print "First(): exiting"
class Second(object):
def __init__(self):
print "Second(): entering"
super(Second, self).__init__()
print "Second(): exiting"
class Third(First, Second):
def __init__(self):
print "Third(): entering"
super(Third, self).__init__()
print "Third(): exiting"
De acuerdo con este artículo sobre Orden de Resolución de Método de Guido van Rossum, la orden para resolver __init__
se calcula (antes de Python 2.3) usando un “recorrido de izquierda a derecha primero en profundidad”:
Third --> First --> object --> Second --> object
Después de eliminar todos los duplicados, excepto el último, obtenemos:
Third --> First --> Second --> object
Entonces, sigamos lo que sucede cuando creamos una instancia de la Third
clase, por ejemplo x = Third()
.
- Según MRO
Third.__init__
ejecuta- huellas dactilares
Third(): entering
- luego
super(Third, self).__init__()
se ejecuta y MRO regresaFirst.__init__
Lo que es llamado.
- huellas dactilares
First.__init__
ejecuta- huellas dactilares
First(): entering
- luego
super(First, self).__init__()
se ejecuta y MRO regresaSecond.__init__
Lo que es llamado.
- huellas dactilares
Second.__init__
ejecuta- huellas dactilares
Second(): entering
- luego
super(Second, self).__init__()
se ejecuta y MRO regresaobject.__init__
Lo que es llamado.
- huellas dactilares
object.__init__
ejecuta (no hay declaraciones de impresión en el código allí)- la ejecución se remonta a
Second.__init__
que luego imprimeSecond(): exiting
- la ejecución se remonta a
First.__init__
que luego imprimeFirst(): exiting
- la ejecución se remonta a
Third.__init__
que luego imprimeThird(): exiting
Esto detalla por qué instanciar Third() da como resultado:
Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
El algoritmo MRO se ha mejorado desde Python 2.3 en adelante para que funcione bien en casos complejos, pero supongo que usar el “recorrido de izquierda a derecha primero en profundidad” + “eliminar duplicados, esperar el último” todavía funciona en la mayoría de los casos (por favor comenten si este no es el caso). ¡Asegúrate de leer la publicación de blog de Guido!
Sección de Reseñas y Valoraciones
Más adelante puedes encontrar las referencias de otros gestores de proyectos, tú igualmente puedes mostrar el tuyo si lo deseas.