Herencia: Taller Integrador
Ya viste la teoría de class Hija(Padre):, super(), sobrescritura de métodos e isinstance()/issubclass() como tema separado. Este taller no enseña nada nuevo: te da 9 problemas cortos más un reto final para que practiques esas herramientas escribiendo jerarquías de clases de cero, hasta que dejen de sentirse como una receta y se sientan como una costumbre.
Bienvenido
Este taller es la práctica integradora de Herencia junto con todo lo anterior: Encapsulación, Clases y Objetos y el resto de guías de POO. No repite la teoría —si algo no te suena (super(), sobrescritura, isinstance()), es el momento de volver a repasarlo antes de seguir.
Este taller asume que ya completaste Herencia. Ningún ejercicio necesita polimorfismo formal ni clases abstractas —nada de eso se explicó todavía—, y tampoco necesitás try/except (manejo de excepciones): esta materia todavía no lo vio.
Cada ejercicio trae un editor vacío: el enunciado te dice qué clases tenés que escribir, y vos escribís el código Python de cero. Presiona ▶ Ejecutar para correrlo de verdad en tu navegador con Pyodide (Python real compilado a WebAssembly). La primera ejecución tarda unos segundos porque descarga el intérprete. Si estás resolviendo el taller desde un lanzamiento de ATENZA, además aparece el botón 📨 Enviar para revisión una vez que el código corra sin errores, para mandar ese ejercicio al docente (no se autocorrige).
En este taller, copiar y pegar código en los editores está deshabilitado (incluido arrastrar y soltar). La idea es que practiques escribiendo la sintaxis vos mismo, no que la traigas de otro lado.
🧬 Herencia básica
Practicás class Hija(Padre): y super().__init__() hasta que reutilizar un constructor deje de sentirse raro.
🔁 Sobrescritura y extensión
Escribís métodos que reemplazan por completo al del padre, y otros que los extienden con super().metodo().
🎯 Reto final
Tres tipos de cuenta bancaria con reglas de retiro distintas, combinando herencia con sobrescritura real.
Zona de pruebas libres
Este bloque queda disponible durante todo el taller. Usalo en cualquier momento para probar una idea suelta, revisar cómo se comporta una clase, o experimentar algo que se te ocurra a mitad de un ejercicio —sin desordenar los bloques numerados.
1. Sintaxis básica y super().__init__()
Tres problemas para practicar class Hija(Padre): y super().__init__() reutilizando el constructor del padre para agregar solo lo que hace falta.
Escribe una clase Empleado con __init__(self, nombre, salario) y un método mostrar(self) que imprima self.nombre + " gana " + str(self.salario). Escribe una clase Vendedor(Empleado) con __init__(self, nombre, salario, comision) que llame super().__init__(nombre, salario) y guarde self.comision. Crea un Vendedor y llamá mostrar() —todavía no lo sobrescribiste, así que debe usar el heredado tal cual.
Escribe una clase Figura con __init__(self, nombre) (guarda self.nombre) y un método area(self) que imprima "Área no definida para " + self.nombre. Escribe una clase Cuadrado(Figura) con __init__(self, lado) que llame super().__init__("Cuadrado") y guarde self.lado —sin sobrescribir area() todavía. Crea un Cuadrado(4) y llamá area(), comprobando que hereda el mensaje genérico porque no lo sobrescribiste aún.
Escribe una clase Dispositivo con __init__(self, marca, precio) y un método encender(self) que imprima self.marca + " encendiendo...". Escribe una clase Telefono(Dispositivo) con __init__(self, marca, precio, capacidad_gb) que llame super().__init__(marca, precio) y guarde self.capacidad_gb. Crea un Telefono, llamá encender() (heredado sin cambios) e imprimí capacidad_gb en una línea aparte.
2. Sobrescritura y extensión con super()
Tres problemas: dos formas distintas de sobrescribir un método heredado —reemplazándolo del todo, o extendiéndolo con super().metodo() para agregarle algo sin perder lo que ya hacía.
Retomando Figura y Cuadrado del ejercicio 1.2: sobrescribí area(self) en Cuadrado para que calcule e imprima "Área: " + str(self.lado ** 2) —reemplazando del todo el mensaje genérico del padre, sin usar super(). Escribí también una clase Circulo(Figura) con __init__(self, radio) (llamando super().__init__("Círculo")) y su propio area(self), que calcule e imprima usando 3.1416 * self.radio ** 2. Creá un objeto de cada clase y llamá area() en los dos.
Retomando Empleado y Vendedor del ejercicio 1.1: sobrescribí mostrar(self) en Vendedor para que primero llame a super().mostrar() y después imprima una segunda línea: "Comisión: " + str(self.comision). Creá un Vendedor y llamá mostrar() —debe imprimir dos líneas, la heredada más la nueva.
Escribe una clase Animal con __init__(self, nombre) y describir(self) que imprima "Soy " + self.nombre. Escribe una clase Ave(Animal) con __init__(self, nombre, puede_volar) que llame super().__init__(nombre) y guarde self.puede_volar. Sobrescribí describir(self) en Ave para que llame super().describir() y después imprima "Puedo volar" si self.puede_volar es True, o "No puedo volar" si es False. Probá con dos objetos Ave: uno con puede_volar=True y otro con puede_volar=False.
3. isinstance(), issubclass() y polimorfismo
Tres problemas para practicar cómo preguntar por la relación entre objetos y clases, y cómo un mismo llamado puede comportarse distinto según el objeto.
Con las clases Animal y Ave del ejercicio 2.3, creá un objeto Ave y un objeto Animal directo (sin usar Ave). Imprimí, en cuatro líneas: isinstance(ave, Animal), isinstance(animal_directo, Ave), issubclass(Ave, Animal) e issubclass(Animal, Ave).
Con Figura, Cuadrado y Circulo del ejercicio 2.1, escribí una función describir_figura(figura) que reciba cualquier objeto Figura, llame a figura.area(), y además: si isinstance(figura, Circulo) es True, imprima "Es un círculo, no tiene lados"; si no, imprima "Tiene " + str(figura.lado) + " unidades de lado". Probala con un Cuadrado y con un Circulo.
Con Empleado y Vendedor (ejercicios 1.1/2.2), escribí una nueva clase Gerente(Empleado) con __init__(self, nombre, salario, bono) que llame super().__init__(nombre, salario) y guarde self.bono (sin sobrescribir nada más). Imprimí cuatro resultados: issubclass(Vendedor, Empleado), issubclass(Gerente, Empleado), issubclass(Vendedor, Gerente) e issubclass(Empleado, object).
Reto final: Tres tipos de cuenta bancaria
Cierra el taller combinando todo: herencia, super(), sobrescritura completa y extendida, y un mismo llamado comportándose distinto según el objeto.
CuentaBancaria:__init__(self, titular, saldo)guarda ambos.depositar(self, monto)—simontono es mayor a 0, imprime"Monto inválido"y no hace nada; si no, sumamontoaself.saldo.retirar(self, monto)—simontoes mayor aself.saldo, imprime"Fondos insuficientes"y no hace nada; si no, restamontodeself.saldo.CuentaAhorro(CuentaBancaria):__init__(self, titular, saldo, tasa_interes)llamasuper().__init__(titular, saldo)y guardaself.tasa_interes. Agregaaplicar_interes(self), que sumaself.saldo * self.tasa_interesaself.saldo.CuentaCorriente(CuentaBancaria):__init__(self, titular, saldo, limite_sobregiro)llamasuper().__init__(titular, saldo)y guardaself.limite_sobregiro. Sobrescriberetirar(self, monto): ahora se permite retirar hastaself.saldo + self.limite_sobregiro—simontosupera eso, imprime"Fondos insuficientes, incluso con sobregiro"y no hace nada; si no, restamontodeself.saldo(el saldo puede terminar negativo, eso es justamente el sobregiro).- Una función
mostrar_saldos(lista_cuentas)que recorra la lista con unfore imprima, para cada una,cuenta.titular + ": " + str(cuenta.saldo)—el mismo atributo funcionando para los tres tipos de objeto, sin preguntar de qué clase es cada una.
Pruébala con este código:
cuenta_ana = CuentaBancaria("Ana", 100)
cuenta_beto = CuentaAhorro("Beto", 200, 0.1)
cuenta_caro = CuentaCorriente("Caro", 50, 100)
cuenta_caro.retirar(120)
print(cuenta_caro.saldo)
cuenta_beto.aplicar_interes()
print(cuenta_beto.saldo)
mostrar_saldos([cuenta_ana, cuenta_beto, cuenta_caro])
-70 220.0 Ana: 100 Beto: 220.0 Caro: -70
Caro retira 120 con saldo 50 y sobregiro 100 (150 disponibles en total): se permite, y el saldo queda en -70. Beto aplica un 10% de interés sobre 200, y su saldo sube a 220.0.
Cheat Sheet del taller
| Herramienta | Para qué la usaste hoy |
|---|---|
class Hija(Padre): | Declarar una subclase que hereda atributos y métodos. |
super().__init__(...) | Reutilizar el constructor del padre en vez de repetirlo. |
Sobrescribir sin super() | Reemplazar del todo el comportamiento heredado (Cuadrado.area()). |
Sobrescribir con super().metodo() | Extender el comportamiento heredado, agregando algo sin perderlo (Vendedor.mostrar(), Ave.describir()). |
isinstance(objeto, Clase) | Preguntar si un objeto es de una clase o de alguna de sus clases base. |
issubclass(Subclase, Clase) | Preguntar si una clase hereda de otra. |
| Mismo atributo/método, clases distintas | mostrar_saldos() y describir_figura() funcionan sin preguntar de qué clase es cada objeto. |
¿Sabías que...?
💳 CuentaCorriente reimplementa, no extiende, retirar()
A diferencia de Vendedor.mostrar() o Ave.describir() (que llaman super()), CuentaCorriente.retirar() del reto final no usa super().retirar() —la regla de negocio cambia tanto (el límite ya no es self.saldo, sino self.saldo + self.limite_sobregiro) que reemplazarlo del todo es más claro que intentar reutilizar la validación del padre.
🔀 Sobrescribir no borra el método del padre
CuentaBancaria.retirar() sigue existiendo y sigue funcionando normal para cualquier objeto CuentaBancaria o CuentaAhorro —sobrescribir en CuentaCorriente solo cambia qué versión corre para los objetos CuentaCorriente, sin afectar a las demás clases de la jerarquía.
🏗️ La siguiente gran idea: polimorfismo
mostrar_saldos() y describir_figura() de este taller ya usan la idea central del polimorfismo: tratar objetos de clases distintas de manera uniforme, dejando que cada uno resuelva el detalle a su manera. Todavía no le pusiste ese nombre formalmente —eso viene en una guía futura.