SDA · Repaso Integrador: Programación Orientada a Objetos (POO) en Python
Materia: Programación y Computación (2º Bachillerato)
Saberes básicos:
- A.2.1. Clases, objetos y constructores. Sobrecarga, encapsulamiento y ocultación.
- A.2.2. Herencia. Subclases y superclases. Interfaces y polimorfismo.
Entorno de trabajo: Python 3.x (IDLE, VS Code, Thonny o Jupyter Notebook)
📖 TEORÍA DE REPASO (Para la Página de Moodle / Entrada de Blog)
Este bloque resume los pilares fundamentales del paradigma de la Programación Orientada a Objetos (POO) en Python estudiados en las sesiones anteriores.
1. Clases, Objetos y Encapsulamiento (Saber Básico A.2.1)
Una clase es la plantilla o molde que define el comportamiento y los datos que tendrá un objeto (instancia).
- Constructor (
__init__): Método especial que se ejecuta automáticamente al instanciar el objeto para inicializar sus atributos. - Encapsulamiento y Ocultación: Mecanismo para restringir el acceso directo a los datos internos de un objeto y proteger su integridad:
- Público (
atributo): Accesible desde cualquier lugar. - Protegido (
_atributo): Indicación por convención de uso interno dentro de la clase y sus subclases. - Privado (
__atributo): Python aplica Name Mangling para dificultar su acceso directo desde el exterior.
- Público (
- Getters y Setters (
@property): Permiten leer y modificar atributos privados aplicando validaciones antes de cambiar un dato.
Python
class Usuario:
def __init__(self, nombre: str, edad: int):
self.nombre = nombre # Atributo público
self.__edad = None # Atributo privado
self.edad = edad # Usa el setter para validar
@property
def edad(self) -> int:
return self.__edad
@edad.setter
def edad(self, valor: int):
if valor >= 0:
self.__edad = valor
else:
raise ValueError("La edad no puede ser negativa.")
2. Herencia y Reutilización de Código (Saber Básico A.2.2)
La herencia permite que una clase hija (subclase) herede atributos y métodos de una clase padre (superclase), evitando la duplicación de código.
super(): Función que permite invocar métodos de la superclase (habitualmente el constructor__init__).- Sobrescritura de métodos (Method Overriding): La subclase redefine un método de la superclase para adaptar su comportamiento.
Python
class Dispositivo:
def __init__(self, marca: str, modelo: str):
self.marca = marca
self.modelo = modelo
def encender((self):
return f"{self.marca} {self.modelo} iniciado."
class Ordenador(Dispositivo):
def __init__(self, marca: str, modelo: str, ram_gb: int):
super().__init__(marca, modelo) # Llamada al constructor padre
self.ram_gb = ram_gb
# Sobrescritura del método
def encender(self):
base = super().encender()
return f"{base} Cargando sistema operativo con {self.ram_gb} GB RAM."
3. Polimorfismo e Interfaces Abstractas (Saber Básico A.2.2)
- Polimorfismo: Capacidad de objetos de diferentes clases para responder al mismo mensaje o llamada a un método de forma específica.
- Interfaces y Clases Abstractas (
abc): Una clase abstracta no se puede instanciar directamente; define un contrato que obliga a todas sus subclases a implementar ciertos métodos marcados con@abstractmethod.
Python
from abc import ABC, abstractmethod
class SensorAbstracto(ABC):
@abstractmethod
def leer_dato(self):
"""Método abstracto que deben implementar obligatoriamente las subclases."""
pass
class SensorTemperatura(SensorAbstracto):
def leer_dato(self):
return 23.5 # Devuelve la lectura en °C
class SensorHumedad(SensorAbstracto):
def leer_dato(self):
return 60 # Devuelve el % de humedad
🛠️ TAREA MOODLE / EJERCICIOS DE REPASO
Título de la tarea:
SDA.2 - Práctica de Repaso: Consolidación de POO, Encapsulamiento, Herencia y Polimorfismo en Python
Descripción para el alumnado:
Markdown
## 💻 SDA.2 - RUTA DE EJERCICIOS DE REPASO POO (Nivel 1 a Nivel 4)
Realiza los siguientes 4 ejercicios programados en Python. Los ejercicios están ordenados de menor a mayor complejidad para que pongas a prueba el dominio progresivo de la Programación Orientada a Objetos.
---
### 📌 EJERCICIO 1 (Nivel 1 - Básico): Encapsulamiento y Validaciones
Crea una clase llamada `NodoIoT` que represente un dispositivo de una red de sensores:
1. **Atributos:**
* `id_nodo` (público, cadena).
* `__ip` (privado, cadena).
* `__bateria` (privado, entero de 0 a 100).
2. **Constructor:** Debe recibir `id_nodo`, `ip` y `bateria`.
3. **Propiedades (Getters y Setters):**
* Define la propiedad `bateria` mediante `@property`.
* En el setter de `bateria`, valida que el valor introducido esté estrictamente entre `0` y `100`. Si el valor no es válido, asigna `0` o lanza un aviso sin modificar el dato.
4. **Método público:**
* `estado()`: Devuelve una cadena con el resumen del nodo (ID, IP y % de batería).
---
### 📌 EJERCICIO 2 (Nivel 2 - Intermedio): Herencia y Sobrescritura
A partir de una superclase base `Vehiculo`:
1. **Superclase `Vehiculo`:**
* Atributos públicos: `matricula` y `marca`.
* Método `describir()`: Devuelve `"Vehículo [marca] - Matrícula: [matricula]"`.
2. **Subclase `VehiculoElectrico`:**
* Hereda de `Vehiculo`.
* Añade el atributo privado `__capacidad_bateria_kwh` y el atributo `__carga_actual_porcentaje`.
* Método `cargar(porcentaje)`: Incrementa la carga actual sin superar el 100%.
* **Sobrescribe** el método `describir()` utilizando `super().describir()` e indicando además la carga actual de la batería.
---
### 📌 EJERCICIO 3 (Nivel 3 - Avanzado): Polimorfismo en Colecciones
Crea un sistema de procesamiento de alertas de seguridad empleando polimorfismo:
1. Crea una clase base `Alerta` con un método `notificar()`.
2. Crea tres subclases que hereden de `Alerta`:
* `AlertaEmail`: Imprime `"[EMAIL] Enviando correo a {destino}: {mensaje}"`.
* `AlertaSMS`: Imprime `"[SMS] Enviando SMS al número {telefono}: {mensaje}"`.
* `AlertaPush`: Imprime `"[PUSH] Notificación enviada al dispositivo {id_dev}: {mensaje}"`.
3. **Prueba polimórfica:** Crea una lista que contenga al menos una instancia de cada tipo de alerta y recorre la lista con un bucle `for`, ejecutando el método `.notificar()` en cada elemento de forma transparente.
---
### 📌 EJERCICIO 4 (Nivel 4 - Desafío Integrador): Clases Abstractas e Interfaces
Diseña la estructura de backend para un sistema de ejecución de tareas automatizadas en un servidor:
1. Importa el módulo `abc` y crea la clase abstracta `TareaAutomatizada(ABC)`.
2. Define el método abstracto `@abstractmethod def ejecutar(self) -> bool:` que debe devolver un valor booleano (`True` si tuvo éxito, `False` si falló).
3. Implementa dos subclases concretas:
* `CopiaDeSeguridad`: Atributo privado `__directorio`. En `ejecutar()` simula la copia imprimiendo la ruta y devuelve `True`.
* `LimpiezaTemporales`: Atributo privado `__archivos_eliminados`. En `ejecutar()` imprime los archivos borrados y devuelve `True`.
4. Crea la clase gestora `GestorDeTareas`:
* Mantiene una lista privada de tareas abstractas `__cola_tareas`.
* Método `agregar_tarea(tarea)`: Verifica que la tarea sea una instancia válida de `TareaAutomatizada` antes de añadirla.
* Método `ejecutar_todas()`: Recorre la cola, ejecuta cada tarea y muestra un informe con cuántas tareas tuvieron éxito.
---
### 📤 Instrucciones de Entrega en Moodle
Pega el código Python completo de los 4 ejercicios en el cuadro de texto de la tarea o sube un único archivo `.py` compilado con todas las soluciones estructuradas.
🔑 SOLUCIONES DE LOS EJERCICIOS
(Sección para autocorrección del alumnado y guía de evaluación para el profesorado).
🟢 Solución – Ejercicio 1 (Nivel 1: Encapsulamiento)
Python
class NodoIoT:
def __init__(self, id_nodo: str, ip: str, bateria: int):
self.id_nodo = id_nodo
self.__ip = ip
self.__bateria = 0 # Inicialización por defecto
self.bateria = bateria # Uso del setter para validar
@property
def bateria(self) -> int:
return self.__bateria
@bateria.setter
def bateria(self, valor: int):
if 0 <= valor <= 100:
self.__bateria = valor
else:
print(f"Error: El nivel de batería {valor}% no es válido (debe ser 0-100).")
def estado(self) -> str:
return f"Nodo: {self.id_nodo} | IP: {self.__ip} | Batería: {self.__bateria}%"
# --- PRUEBA EJERCICIO 1 ---
if __name__ == "__main__":
nodo1 = NodoIoT("SENSOR_01", "192.168.1.50", 85)
print(nodo1.estado())
# Intento de asignación inválida
nodo1.bateria = 150
print(nodo1.estado()) # Mantiene el valor anterior (85)
🟢 Solución – Ejercicio 2 (Nivel 2: Herencia y super())
Python
class Vehiculo:
def __init__(self, matricula: str, marca: str):
self.matricula = matricula
self.marca = marca
def describir() -> str:
return f"Vehículo {self.marca} - Matrícula: {self.matricula}"
class VehiculoElectrico(Vehiculo):
def __init__(self, matricula: str, marca: str, capacidad_kwh: float):
super().__init__(matricula, marca)
self.__capacidad_bateria_kwh = capacidad_kwh
self.__carga_actual_porcentaje = 100.0 # Carga inicial completa
def cargar(self, porcentaje: float):
if porcentaje > 0:
self.__carga_actual_porcentaje = min(100.0, self.__carga_actual_porcentaje + porcentaje)
print(f"Cargado. Nivel actual: {self.__carga_actual_porcentaje}%")
def describir() -> str:
info_base = super().describir()
return f"{info_base} | [EV] Batería: {self.__carga_actual_porcentaje}% ({self.__capacidad_bateria_kwh} kWh)"
# --- PRUEBA EJERCICIO 2 ---
if __name__ == "__main__":
coche_ev = VehiculoElectrico("1234-LMN", "Tesla", 75.0)
print(coche_ev.describir())
coche_ev._VehiculoElectrico__carga_actual_porcentaje = 30.0 # Simulación de descarga
coche_ev.cargar(25)
print(coche_ev.describir())
🟢 Solución – Ejercicio 3 (Nivel 3: Polimorfismo)
Python
class Alerta:
def __init__(self, mensaje: str):
self.mensaje = mensaje
def notificar(self):
raise NotImplementedError("Las subclases deben implementar el método notificar().")
class AlertaEmail(Alerta):
def __init__(self, mensaje: str, destino: str):
super().__init__(mensaje)
self.destino = destino
def notificar(self):
print(f"[EMAIL] Enviando correo a {self.destino}: '{self.mensaje}'")
class AlertaSMS(Alerta):
def __init__(self, mensaje: str, telefono: str):
super().__init__(mensaje)
self.telefono = telefono
def notificar(self):
print(f"[SMS] Enviando SMS al número {self.telefono}: '{self.mensaje}'")
class AlertaPush(Alerta):
def __init__(self, mensaje: str, id_dispositivo: str):
super().__init__(mensaje)
self.id_dispositivo = id_dispositivo
def notificar(self):
print(f"[PUSH] Notificación enviada al dispositivo {self.id_dispositivo}: '{self.mensaje}'")
# --- PRUEBA EJERCICIO 3 (POLIMORFISMO) ---
if __name__ == "__main__":
bandeja_alertas = [
AlertaEmail("Fallo de conexión en servidor central", "admin@empresa.com"),
AlertaSMS("Temperatura crítica en CPD (42°C)", "+34600112233"),
AlertaPush("Nueva actualización de firmware disponible", "DEV_98122")
]
print("--- PROCESANDO COLA DE ALERTAS ---")
for alerta in bandeja_alertas:
alerta.notificar() # Llamada polimórfica
🟢 Solución – Ejercicio 4 (Nivel 4: Clases Abstractas e Interfaces)
Python
from abc import ABC, abstractmethod
# 1. Clase Abstracta / Interfaz
class TareaAutomatizada(ABC):
@abstractmethod
def ejecutar(self) -> bool:
"""Método abstracto obligatorio."""
pass
# 2. Subclases Concretas
class CopiaDeSeguridad(TareaAutomatizada):
def __init__(self, directorio_origen: str):
self.__directorio = directorio_origen
def ejecutar(self) -> bool:
print(f"-> Creando copia de seguridad del directorio: '{self.__directorio}'... [OK]")
return True
class LimpiezaTemporales(TareaAutomatizada):
def __init__(self, cantidad_archivos: int):
self.__archivos_eliminados = cantidad_archivos
def ejecutar(self) -> bool:
print(f"-> Eliminados {self.__archivos_eliminados} archivos temporales del sistema... [OK]")
return True
# 3. Clase Gestora
class GestorDeTareas:
def __init__(self):
self.__cola_tareas = []
def agregar_tarea(self, tarea: TareaAutomatizada):
if isinstance(tarea, TareaAutomatizada):
self.__cola_tareas.append(tarea)
else:
print("Error: El objeto no implementa la interfaz TareaAutomatizada.")
def ejecutar_todas(self):
print("=== INICIANDO EJECUCIÓN DE TAREAS EN BATCH ===")
exitosas = 0
for idx, tarea in enumerate(self.__cola_tareas, start=1):
print(f"Tarea #{idx}:", end=" ")
if tarea.ejecutar():
exitosas += 1
print(f"=== FINALIZADO: {exitosas}/{len(self.__cola_tareas)} tareas completadas con éxito. ===")
# --- PRUEBA EJERCICIO 4 ---
if __name__ == "__main__":
gestor = GestorDeTareas()
gestor.agregar_tarea(CopiaDeSeguridad("/var/www/html"))
gestor.agregar_tarea(LimpiezaTemporales(142))
gestor.ejecutar_todas()
📊 Rúbrica de evaluación para el profesorado (Moodle)
| Criterio | Excelente (100%) | En proceso (50%) | No logrado / Incompleto (0%) |
| Encapsulamiento y Setters (E1) (2.5 ptos) | Oculta atributos privados correctamente y aplica validaciones lógicas en el setter @property. | Implementa el setter pero no realiza la validación de rangos o accede directamente a variables privadas. | No encapsula los atributos ni define propiedades. |
Herencia y reuso con super() (E2) (2.5 ptos) | Extiende la superclase reutilizando el constructor y métodos mediante super(). | Utiliza herencia pero duplica código del padre o no usa la función super(). | No aplica el concepto de herencia. |
| Polimorfismo en colecciones (E3) (2.5 ptos) | Diseña la jerarquía de alertas y recorre la colección ejecutando el método común en cada subclase. | Implementa las subclases pero evalúa los tipos manualmente en el bucle (if type == ...) en vez de aplicar polimorfismo. | No implementa la llamada polimórfica. |
| Diseño con clases abstractas (E4) (2.5 ptos) | Utiliza el módulo abc con @abstractmethod y valida el tipo en la clase gestora. | Implementa la clase abstracta pero no obliga al uso de @abstractmethod o no valida en la clase gestora. | No hace uso de abstracción ni interfaces. |
