SdA A.2. Programación Orientada a Objetos (POO) en Python
A.2.1. Clases, Objetos, Constructores, Sobrecarga, Encapsulamiento y Ocultación
¡Bienvenidos al paradigma que cambió la forma de crear software!
Hasta ahora hemos programado de forma estructurada/procedimental: creábamos variables y funciones que las manipulaban. Sin embargo, cuando el software crece (pensad en un videojuego o un sistema bancario), este enfoque se vuelve inmanejable.
La Programación Orientada a Objetos (POO) nos permite modelar el mundo real en el código combinando datos (atributos) y comportamientos (métodos) dentro de entidades llamadas objetos.
1. Conceptos Fundamentales: Clases, Objetos y el Constructor (__init__)
- Clase: Es el plano o la plantilla arquitectónica. Define qué datos tendrá una entidad y qué podrá hacer, pero no ocupa memoria con datos reales.
- Objeto (o Instancia): Es la realización concreta del plano. Si la clase es el plano de un coche, el objeto es el coche real fabricado aparcado en el garaje.
- Constructor (
__init__): Es un método especial que se ejecuta automáticamente cuando creamos un nuevo objeto. Se encarga de dar valores iniciales a sus atributos. - El parámetro
self: Hace referencia a la instancia concreta que está ejecutando el método en ese momento. Es obligatorio como primer parámetro de todos los métodos de clase.
Python
# Definición de la Clase (El plano)
class Coche:
# Constructor: inicializa los atributos del objeto
def __init__(self, marca: str, modelo: str, combustible: str):
self.marca = marca # Atributo público
self.modelo = modelo # Atributo público
self.combustible = combustible
self.velocidad = 0 # Atributo con valor por defecto
# Método de comportamiento
def acelerar(self, incremento: int):
self.velocidad += incremento
print(f"El {self.marca} {self.modelo} ha acelerado a {self.velocidad} km/h.")
# Creación de Objetos (Instanciación)
coche1 = Coche("Toyota", "Yaris", "Híbrido")
coche2 = Coche("Ford", "Focus", "Gasolina")
# Uso de métodos
coche1.acelerar(30) # Imprime: El Toyota Yaris ha acelerado a 30 km/h.
2. Sobrecarga en Python
En lenguajes como Java o C++, la sobrecarga de métodos (overloading) permite definir varios métodos con el mismo nombre pero con distintos parámetros. En Python esto no funciona así directamente: si defines dos métodos con el mismo nombre, el segundo sobrescribirá al primero.
En Python logramos la sobrecarga mediante dos mecanismos:
A. Sobrecarga mediante parámetros por defecto o variables (*args, **kwargs)
Permite llamar a un mismo método o constructor con diferente número de argumentos.
Python
class Calculadora:
# Sobrecarga simulada mediante parámetros opcionales
def sumar(self, a, b, c=None):
if c is not None:
return a + b + c
return a + b
calc = Calculadora()
print(calc.sumar(5, 10)) # Resultado: 15
print(calc.sumar(5, 10, 20)) # Resultado: 35
B. Sobrecarga de Operadores y Métodos Mágicos (Dunder Methods)
Python permite redefinir cómo actúan los operadores (+, -, ==, etc.) o funciones globales (print(), len()) sobre nuestros objetos usando métodos especiales con doble guion bajo (__):
__str__(self): Define cómo se representa el objeto cuando lo imprimimos conprint().__add__(self, otro): Sobrecarga el operador suma (+).
Python
class Vector2D:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
# Sobrecarga del método print()
def __str__(self):
return f"Vector({self.x}, {self.y})"
# Sobrecarga del operador +
def __add__(self, otro_vector):
return Vector2D(self.x + otro_vector.x, self.y + otro_vector.y)
v1 = Vector2D(3, 4)
v2 = Vector2D(1, 2)
v3 = v1 + v2 # Invoca automáticamente v1.__add__(v2)
print(v3) # Invoca automáticamente __str__: Imprime "Vector(4.0, 6.0)"
3. Encapsulamiento y Ocultación de Datos
El encapsulamiento consiste en agrupar datos y métodos reduciendo el acceso directo desde el exterior para proteger la integridad del objeto (evitar que se asignen datos inválidos como una velocidad negativa o un saldo menor que cero).
En Python no existen las palabras clave public, protected o private. En su lugar, usamos una convención de nombres:
- Público (
atributo): Accesible libremente desde dentro y fuera de la clase. - Protegido (
_atributo): Indicación visual (convención) para que otros programadores sepan que no se debe acceder desde fuera salvo en clases derivadas. - Privado / Ocultación (
__atributo): Usa dos guiones bajos iniciales. Python aplica Name Mangling (cambio de nombre interno), haciendo que el atributo sea “invisible” en un acceso directo desde fuera.
Acceso Seguro: Getters, Setters y @property
Para interactuar con atributos ocultos/privados utilizamos métodos específicos de lectura (getters) y modificación (setters), o el decorador @property idóneo en Python:
Python
class CuentaBancaria:
def __init__(self, titular: str, saldo_inicial: float):
self.titular = titular
self.__saldo = saldo_inicial # Atributo PRIVADO / OCULTO (doble guion)
# Propiedad de Lectura (Getter)
@property
def saldo(self):
return self.__saldo
# Propiedad de Escritura con validación (Setter)
@saldo.setter
def saldo(self, nuevo_saldo):
if nuevo_saldo < 0:
print("Error: El saldo no puede ser negativo.")
else:
self.__saldo = nuevo_saldo
def ingresar(self, cantidad: float):
if cantidad > 0:
self.__saldo += cantidad
# Probando la ocultación
cuenta = CuentaBancaria("Lucía", 1000)
# Intentar acceder directamente falla o no modifica el privado:
# print(cuenta.__saldo) -> AttributeError: 'CuentaBancaria' object has no attribute '__saldo'
# Uso correcto a través del Getter/Setter
print(f"Saldo actual: {cuenta.saldo} €") # 1000 €
cuenta.saldo = -500 # Muestra mensaje de error
cuenta.ingresar(200)
print(f"Nuevo saldo: {cuenta.saldo} €") # 1200 €
Parte 2: Ejercicios para Moodle (Tarea del Alumnado)
Tarea Práctica: Modelado POO en Python — Sistema de Personajes y Gestión Bancaria
INSTRUCCIONES DE ENTREGA
Debes entregar un archivo comprimido ZIP titulado POO_A21_NombreApellido.zip que contenga:
ejercicio1_banco.py: Código fuente de la Actividad 1.ejercicio2_videojuego.py: Código fuente de la Actividad 2.- Un documento PDF o imagen con las capturas de pantalla de la ejecución por consola de ambos programas donde se pruebe todo su funcionamiento.
ACTIVIDAD 1: Sistema de Gestión de Cuentas de Usuario (Puntuación: 5 puntos)
Objetivo: Aplicar los conceptos de Clase, Constructor, Ocultación (__) y uso de Getters/Setters con @property.
Crea una clase llamada UsuarioPlataforma que gestione las cuentas de una plataforma educativa o de streaming.
Requisitos de la Clase:
- Atributos:
nombre_usuario(público, cadena de texto).email(público, cadena de texto).__password(privado/oculto, cadena de texto).__puntos(privado/oculto, entero inicializado en 0).
- Constructor (
__init__):- Debe recibir obligatoriamente
nombre_usuario,emailypassword. El saldo de__puntosse inicia a 0 por defecto.
- Debe recibir obligatoriamente
- Ocultación y Métodos de Control:
- Crea un Getter usando
@propertypara leerpuntos. - Crea un método
cambiar_password(password_actual, nueva_password)que modifique__passwordsolo si la contraseña actual introducida coincide con la guardada. - Crea un método
ganar_puntos(cantidad)que sume puntos únicamente si la cantidad introducida es un entero mayor que cero.
- Crea un Getter usando
- Representación:
- Sobrecarga el método
__str__para que al imprimir el objeto devuelva:"Usuario: [nombre_usuario] | Email: [email] | Puntos: [puntos]"
- Sobrecarga el método
Script de Prueba (al final del archivo .py):
Instancia 2 usuarios diferentes, realiza un cambio de contraseña correcto y otro incorrecto, suma puntos a un usuario e imprime ambos objetos usando print().
ACTIVIDAD 2: Sistema de Batallas de Personajes con Sobrecarga (Puntuación: 5 puntos)
Objetivo: Implementar Sobrecarga de Métodos/Constructores (vía parámetros por defecto) y Sobrecarga de Operadores (__add__, __str__).
Diseña la estructura de objetos para un videojuego de rol (RPG).
Requisitos de la Clase Personaje:
- Atributos:
nombre(cadena de texto).nivel(entero).vida(entero o flotante).fuerza(entero).
- Constructor y Sobrecarga por Parámetros por Defecto:
- El constructor debe permitir crear un personaje proporcionando solo su
nombre(en cuyo caso nivel=1, vida=100 y fuerza=10) O indicando explícitamente todos los valores.
- El constructor debe permitir crear un personaje proporcionando solo su
- Sobrecarga de Métodos Especiales:
__str__(self): Debe devolver la ficha técnica formateada del personaje.Ejemplo:"[Guerrero] Nivel: 5 | Vida: 120 HP | Fuerza: 25"__add__(self, otro_personaje)(Fusión de Personajes):Sobrecarga el operador suma (+) de forma que al sumar dos personajes (p3 = p1 + p2):- El nuevo nombre sea la combinación de ambos (ej.
"Thor" + "Loki"$\rightarrow$"Thor-Loki"). - El nuevo nivel sea la media de los niveles + 1.
- La nueva vida sea la suma de ambas vidas.
- La nueva fuerza sea el valor máximo entre las dos fuerzas.
- Retorne un nuevo objeto
Personajecon estas características.
- El nuevo nombre sea la combinación de ambos (ej.
- Método de Combate:
- Crea un método
atacar(otro_personaje)que reste a lavidadel objetivo una cantidad igual a lafuerzadel atacante. Muestra un mensaje formateado informando del combate.
- Crea un método
Script de Prueba (al final del archivo .py):
- Crea un personaje $P1$ (“Hércules”) usando los parámetros por defecto.
- Crea un personaje $P2$ (“Aquiles”) especificando Nivel 3, Vida 150 y Fuerza 30.
- Haz que $P2$ ataque a $P1$.
- Imprime el estado de $P1$ tras el ataque.
- Fusiona a $P1$ y $P2$ usando el operador
+(fused = P1 + P2) e imprime la ficha del nuevo personaje resultante.
CRITERIOS DE EVALUACIÓN (RÚBRICA MOODLE)
| Criterio | Excelente (100%) | Aceptable (60%) | Insuficiente (0-30%) |
| Modelado y Sintaxis POO (30%) | Clases, atributos, constructor __init__ y self implementados correctamente sin errores. | Clases funcionales pero con errores menores en el uso de self o nomenclatura. | Uso incorrecto del paradigma POO o errores que impiden la ejecución. |
| Encapsulamiento y Ocultación (30%) | Uso correcto de atributos privados (__), getters, setters con @property y validaciones lógicas. | Usa atributos privados pero no implementa los getters/setters correctamente o falta validación. | No aplica ocultación de datos o deja acceso directo a variables sensibles. |
| Sobrecarga y Métodos Mágicos (30%) | Implementación perfecta de __str__, __add__ y parámetros por defecto/opcionales. | Sobrecarga parcialmente conseguida o con errores de devolución en el operador +. | No aplica sobrecarga o reemplaza el operador suma con un método común. |
| Script de Prueba y Documentación (10%) | Casos de prueba completos, código limpio e indizado con comentarios explicativos. | Faltan algunos casos de prueba solicitados o el código carece de comentarios. | No entrega script de prueba o el programa no se puede probar. |
