Aprende leyendo en orden

Herencia múltiple y MRO — Heredar de más de un padre

Aprende la herencia múltiple en Python. Combina padres con class Hijo(A, B):, mira cómo el orden de resolución de métodos (MRO) elige entre métodos del mismo nombre, y verifica el orden con __mro__.

La última vez cubrimos la herencia simple, la sobrescritura y super(). Python te permite heredar de más de un padre al mismo tiempo — eso es la herencia múltiple. Esta vez cubrimos la sintaxis y la regla que decide qué padre se llama cuando varios tienen el mismo nombre de método: el orden de resolución de métodos (MRO).

La sintaxis básica

La sintaxis es simple: lista las clases padres separadas por comas. Escribir class Duck(Animal, Swimmer, Flyer): significa que Duck hereda atributos y métodos de todos los Animal, Swimmer y Flyer.

En el ejemplo de abajo, Animal lleva el nombre y el comportamiento de habla, Swimmer añade nadar, Flyer añade volar — y Duck agrupa los tres en una criatura completa.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass


class Swimmer:
    def swim(self):
        return f"{self.name} está nadando"


class Flyer:
    def fly(self):
        return f"{self.name} está volando"


class Duck(Animal, Swimmer, Flyer):    # herencia múltiple
    def speak(self):
        return f"{self.name} hace cuac"


duck = Duck("Donald")
print(duck.speak())   # Donald hace cuac
print(duck.swim())    # Donald está nadando
print(duck.fly())     # Donald está volando
Duck hereda de tres padres
Animalname / speakSwimmerswimFlyerflyDuckspeak (override)heredaheredahereda
Duck hereda atributos y __init__ de Animal, swim de Swimmer y fly de Flyer. Solo speak es sobrescrito por Duck mismo.

Construye el Duck que agrupa tres padres.

① Define class Animal: con __init__(self, name) que asigne self.name = name.

② Dentro de class Swimmer:, define swim(self) que retorne f"{self.name} está nadando". Haz lo mismo para Flyer con fly(self).

③ Define class Duck(Animal, Swimmer, Flyer): con speak(self) que retorne f"{self.name} hace cuac".

④ Construye duck = Duck("Donald") y print los valores devueltos por speak(), swim() y fly().

(Cuando se ejecute correctamente, aparecerá la explicación.)

Editor Python

Ejecutar el código para ver el resultado

Orden de resolución de métodos (MRO) — ¿Qué padre gana?

Las cosas se complican cuando varios padres tienen un método con el mismo nombre. Si Swimmer y Flyer definieran ambos un método move, ¿cuál se ejecuta cuando llamas a move en una instancia Duck?

Python usa el orden de resolución de métodos (MRO) — un orden de búsqueda fijo — para recorrer las clases de arriba abajo y ejecutar la primera coincidencia. Para herencia múltiple, el orden es de izquierda a derecha como se escribe en class Duck(A, B, C):. Así que el método del mismo nombre de A gana; si no está, se prueba B, luego C.

class Swimmer:
    def move(self):
        return "nadando"

class Flyer:
    def move(self):
        return "volando"

class Duck(Swimmer, Flyer):    # izquierda gana = Swimmer.move
    pass

class Goose(Flyer, Swimmer):   # invierte el orden = resultado distinto
    pass

print(Duck().move())   # nadando
print(Goose().move())  # volando
El MRO recorre (A, B, C) de izquierda a derecha
Duck().move()¿move enDuck?no→ siguiente→ ejecuta¿move enSwimmer?→ ejecuta
Para Duck(Swimmer, Flyer), Python mira Swimmer primero, luego Flyer. Si ambos definen move, gana Swimmer.move. Reordena los padres y el resultado cambia.

En CPython puedes leer el orden real de búsqueda con NombreDeClase.__mro__ (el runtime aquí es MicroPython, que no expone el atributo __mro__, así que el snippet de abajo es solo una referencia para lo que verías en Python normal).

# Lo que CPython mostraría
class Swimmer:
    pass
class Flyer:
    pass
class Duck(Swimmer, Flyer):
    pass

for cls in Duck.__mro__:
    print(cls.__name__)
# Duck
# Swimmer
# Flyer
# object

¿Qué es ese object al final?

Cada clase en Python finalmente hereda de la clase nativa object. Aun cuando no lo escribes, class Foo: internamente es class Foo(object):. Por eso __mro__ siempre termina con object.

Confirma con un experimento rápido que el orden en el que listas los padres cambia el resultado.

① Dentro de class Swimmer:, define move(self) que retorne "nadando". Haz lo mismo para Flyer con "volando".

② Define class Duck(Swimmer, Flyer): y class Goose(Flyer, Swimmer):, ambos con solo pass.

print el resultado de Duck().move() y Goose().move() — observa cómo invertir el orden de los padres invierte el resultado.

Editor Python

Ejecutar el código para ver el resultado

Herencia en diamante y linealización C3

Otra forma que aparece a menudo es la herencia en diamante: B y C heredan cada uno de A, y D hereda tanto de B como de C — al dibujar el grafo de herencia obtienes una forma literal de diamante.

La forma de la herencia en diamante
A(padre común)Bhereda de AChereda de ADhereda B y Cheredaheredaheredahereda
A es el ancestro común. B y C heredan cada uno de A. D luego hereda de ambos B y C — las flechas forman un diamante.

Python calcula el MRO con un algoritmo llamado linealización C3, que produce el orden inteligente «probar primero los descendientes (B, C) y luego subir al ancestro común (A) al final».

class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):       # diamante
    pass

print(D().method())  # B

En este ejemplo, D().method() retorna "B" porque el MRO es D → B → C → A. Si B no definiera method, se ejecutaría el de C; sin eso, el de A. «Primero a la izquierda, pero el ancestro común al final» — ese es el truco para la herencia en diamante.

Llamar al método de un padre específico por nombre de clase

Cuando específicamente quieres llamar al método de un padre particular — no solo al que gana el MRO — super() solo te da la siguiente clase en el MRO, así que puedes llamar a lo sumo una versión del padre. En cambio, usa ClasePadre.method(self, ...) para apuntar exactamente a la que quieres.

Como la llamada va por el lado de la clase, tienes que pasar self tú mismo como primer argumento — la única peculiaridad a recordar.

super() vs llamar por nombre de clase
C().hello()super().hello()orden MRO(solo uno)A.hello(self)B.hello(self)ambos A y Bllamables
super() ejecuta solo un método (el siguiente en el MRO). Llamar por nombre de clase como A.hello(self) te permite apuntar a un padre específico independientemente del MRO — útil cuando quieres invocar varios métodos del padre en secuencia.
class A:
    def hello(self):
        print("hola desde A")

class B:
    def hello(self):
        print("hola desde B")

class C(A, B):
    def hello(self):
        A.hello(self)        # llamar explícitamente al hello de A
        B.hello(self)        # llamar explícitamente al hello de B
        print("hola desde C")

C().hello()
# hola desde A
# hola desde B
# hola desde C

Escribe una clase C que llame a los hello de ambos padres por nombre.

① Define class A: con hello(self) que ejecute print("hola desde A").

② Define class B: con hello(self) que ejecute print("hola desde B").

③ Define class C(A, B): con hello(self) que llame a A.hello(self)B.hello(self)print("hola desde C") en ese orden (no olvides pasar self).

④ Ejecuta C().hello() y confirma que se imprimen tres líneas.

Editor Python

Ejecutar el código para ver el resultado

Potente, pero úsala con moderación

La herencia múltiple es conveniente, pero tienes que mantener el MRO en la cabeza todo el tiempo para seguir el comportamiento — eso es un coste cognitivo real. En la práctica, en lugar de forzar la herencia múltiple, usar un solo padre y mantener pequeñas clases de funciones como atributos (composición) suele ser más claro. Reserva la herencia múltiple para casos como mezclar capacidades independientes como Swimmer / Flyer.

Lo que quieresCómo escribirlo
Heredar de varios padresclass Hijo(A, B): ...
Ver qué padre ganaNombreDeClase.__mro__ (CPython)
Llamar solo al siguiente del MROsuper().method(...)
Apuntar a un padre específicoClasePadre.method(self, ...)
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1En class Duck(Swimmer, Flyer):, si ambos padres definen un método move, ¿cuál gana?

Pregunta 2¿Cuál es la forma correcta de leer el orden de resolución de métodos (MRO) de una clase? (CPython)

Pregunta 3En herencia múltiple, ¿cuál es la forma correcta de llamar al método de un padre específico por su nombre?