Juan Diego Andrés PRADA··RAMÍREZ Entrar
Lección 4 de 7

Herencia y polimorfismo

¿Qué es?

La herencia permite crear una clase nueva a partir de otra existente, heredando sus atributos y métodos y añadiendo o cambiando lo que haga falta. La clase de la que se hereda es la clase base (o superclase); la nueva es la subclase.

El polimorfismo es la capacidad de tratar objetos de distintas clases de la misma manera, siempre que compartan una interfaz común. Si varios objetos responden al método descripcion(), puedo recorrerlos a todos y llamar descripcion() sin saber de qué clase es cada uno.

¿Cómo funciona?

Para heredar, escribes class Subclase(Base):. La subclase recibe todo lo de la base. Con super().__init__(...) llamas al constructor de la base para no repetir su inicialización. Para cambiar un comportamiento heredado, redefines (sobrescribes) el método en la subclase. El polimorfismo aparece solo: si distintas clases tienen un método con el mismo nombre, Python ejecuta el que corresponde al objeto real en tiempo de ejecución.

¿Para qué sirve?

Sirve para reutilizar comportamiento común sin copiar y pegar, y para escribir código que funciona con familias enteras de objetos. En el sistema de notas tendremos distintos tipos de estudiante (regular, becado) que comparten casi todo pero difieren en un detalle.

Qué pieza del sistema construimos

Construimos una jerarquía: una clase base Persona con nombre, y dos subclases de estudiante, EstudianteRegular y EstudianteBecado, que comparten el manejo de notas pero calculan distinto su estado académico. Luego generamos un reporte polimórfico que recorre cualquier mezcla de estudiantes.

Una clase base común

class Persona:
 def __init__(self, nombre):
 self.nombre = nombre

 def presentarse(self):
 return f"Soy {self.nombre}"

Heredando y usando super()

Creamos Estudiante como subclase de Persona. Reutilizamos su __init__ con super().

class Estudiante(Persona):
 def __init__(self, nombre):
 super().__init__(nombre) # inicializa lo de Persona (self.nombre)
 self.notas = []

 def agregar_nota(self, valor):
 self.notas.append(valor)

 @property
 def promedio(self):
 if not self.notas:
 return 0.0
 return sum(self.notas) / len(self.notas)

 def estado(self):
 return "aprobado" if self.promedio >= 3.0 else "reprobado"

super().__init__(nombre) ejecuta el constructor de Persona, que asigna self.nombre. Así no repetimos esa línea y, si Persona cambia, la subclase se beneficia automáticamente.

Sobrescribiendo comportamiento

Un estudiante becado necesita un promedio más alto para conservar el estado de aprobado. Heredamos casi todo y solo redefinimos estado.

class EstudianteBecado(Estudiante):
 def __init__(self, nombre, minimo=3.5):
 super().__init__(nombre)
 self.minimo = minimo

 def estado(self): # sobrescribe el de Estudiante
 return "aprobado" if self.promedio >= self.minimo else "en riesgo"

EstudianteBecado hereda agregar_nota y promedio tal cual; solo cambia estado. Llamamos super().__init__(nombre) para reutilizar la inicialización del estudiante y luego añadimos su atributo propio minimo.

Polimorfismo en acción

Ahora lo bonito: una sola función genera el reporte sin importar el tipo concreto.

def reporte(estudiantes):
 for est in estudiantes:
 print(f"{est.presentarse()} | promedio {est.promedio:.2f} | {est.estado()}")

ana = Estudiante("Ana")
ana.agregar_nota(4.0)

luis = EstudianteBecado("Luis")
luis.agregar_nota(3.2)

reporte([ana, luis])
# Soy Ana | promedio 4.00 | aprobado
# Soy Luis | promedio 3.20 | en riesgo

La función reporte no pregunta "¿eres regular o becado?". Simplemente llama est.estado() y Python ejecuta la versión correcta para cada objeto. Eso es polimorfismo.

Esto se conoce como duck typing: "si camina como pato y suena como pato, es un pato". A Python no le importa la clase exacta; le importa que el objeto tenga los métodos que vas a usar (presentarse, promedio, estado).

Error común: olvidar super().__init__(...) en la subclase. Si lo omites, los atributos de la base (como self.nombre) nunca se crean y obtienes un AttributeError al usarlos. Siempre llama al constructor de la base cuando defines tu propio __init__.

Tech English: inheritance = herencia, override = sobrescribir, base class / subclass = clase base / subclase, duck typing = tipado pato.

Ejercicios

  1. Tercer tipo de estudiante. Diseña EstudianteOyente, que hereda de Estudiante pero cuyo estado() siempre devuelve "sin evaluación" (no aprueba ni reprueba). Demuestra que la función reporte lo procesa sin cambiar ni una línea.

  2. Método extendido, no reemplazado. Diseña una subclase de Persona llamada Profesor cuyo presentarse() reutilice el de la base con super().presentarse() y le añada " y soy profesor". Explica la diferencia entre extender y reemplazar un método.

Tu progreso se guarda en este navegador. Inicia sesión para guardarlo en tu cuenta y verlo desde cualquier dispositivo.