Pregunta 1En class Duck(Swimmer, Flyer):, si ambos padres definen un método move, ¿cuál gana?
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 atributos y __init__ de Animal, swim de Swimmer y fly de Flyer. Solo speak es sobrescrito por Duck mismo.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
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.
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.
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() 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
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 quieres | Cómo escribirlo |
|---|---|
| Heredar de varios padres | class Hijo(A, B): ... |
| Ver qué padre gana | NombreDeClase.__mro__ (CPython) |
| Llamar solo al siguiente del MRO | super().method(...) |
| Apuntar a un padre específico | ClasePadre.method(self, ...) |
Verificación de conocimientos
Responde cada pregunta una a una.
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?