Clases y Objetos: El Paradigma OOP desde Cero
Hasta ahora hemos escrito codigo procedural: instrucciones que se ejecutan de arriba a abajo. La Programacion Orientada a Objetos (OOP) organiza el codigo en torno a objetos que tienen datos (atributos) y comportamiento (metodos).
Por que OOPImagina un juego con 100 personajes. Sin OOP necesitas variables separadas para cada uno. Con OOP defines una clase (plantilla) y creas todos los objetos que necesites:
class Personaje:
def __init__(self, nombre, vida, ataque):
self.nombre = nombre
self.vida = vida
self.ataque = ataque
guerrero = Personaje('Guerrero', 100, 25)
mago = Personaje('Mago', 80, 40)
Que es una clase
Una clase es una plantilla. Un objeto es una instancia concreta de esa clase.
- Clase = Plano de una casa / Objeto = La casa
- Clase = Receta de pizza / Objeto = La pizza
class CuentaBancaria:
def __init__(self, titular, saldo_inicial=0):
self.titular = titular
self.saldo = saldo_inicial
self.transacciones = []
def depositar(self, monto):
if monto <= 0:
raise ValueError('El monto debe ser positivo')
self.saldo += monto
self.transacciones.append(('deposito', monto))
print(f'Deposito exitoso. Saldo: {self.saldo}')
def retirar(self, monto):
if monto > self.saldo:
raise ValueError('Saldo insuficiente')
self.saldo -= monto
self.transacciones.append(('retiro', monto))
print(f'Retiro exitoso. Saldo: {self.saldo}')
def ver_saldo(self):
print(f'Titular: {self.titular}')
print(f'Saldo: {self.saldo}')
def historial(self):
print(f'Historial de {self.titular}:')
for tipo, monto in self.transacciones:
signo = '+' if tipo == 'deposito' else '-'
print(f' {signo} {monto}')
Crear objetos
cuenta_ana = CuentaBancaria('Ana Garcia', 1000)
cuenta_carlos = CuentaBancaria('Carlos Lopez')
cuenta_ana.ver_saldo()
cuenta_ana.depositar(500)
cuenta_ana.retirar(200)
cuenta_ana.historial()
cuenta_carlos.depositar(300)
print(cuenta_ana.saldo) # 1300
print(cuenta_carlos.saldo) # 300
El parametro self
self es una referencia al objeto actual. Cuando Python ejecuta cuenta_ana.depositar(500), internamente lo convierte en CuentaBancaria.depositar(cuenta_ana, 500).
class Coche:
def __init__(self, marca, modelo, anio):
self.marca = marca
self.modelo = modelo
self.anio = anio
self.velocidad = 0
def acelerar(self, incremento):
self.velocidad += incremento
print(f'{self.marca} {self.modelo}: {self.velocidad} km/h')
def frenar(self):
self.velocidad = max(0, self.velocidad - 20)
print(f'Frenando: {self.velocidad} km/h')
mi_coche = Coche('Toyota', 'Corolla', 2023)
tu_coche = Coche('Honda', 'Civic', 2022)
mi_coche.acelerar(60)
tu_coche.acelerar(80)
print(mi_coche.velocidad) # 60
print(tu_coche.velocidad) # 80
Atributos de clase vs atributos de instancia
class Estudiante:
universidad = 'Universidad Nacional' # Atributo de CLASE
total_estudiantes = 0
def __init__(self, nombre, carrera):
self.nombre = nombre # Atributo de INSTANCIA
self.carrera = carrera
self.notas = []
Estudiante.total_estudiantes += 1
def agregar_nota(self, nota):
self.notas.append(nota)
def promedio(self):
if not self.notas:
return 0
return sum(self.notas) / len(self.notas)
est1 = Estudiante('Ana', 'Informatica')
est2 = Estudiante('Carlos', 'Medicina')
est1.agregar_nota(90)
est1.agregar_nota(85)
print(f'Promedio: {est1.promedio():.1f}')
print(est1.universidad) # Universidad Nacional
print(Estudiante.total_estudiantes) # 2
Encapsulacion: Proteger datos
class CuentaSegura:
def __init__(self, titular, pin, saldo=0):
self.titular = titular
self.__pin = pin # privado (doble guion bajo)
self.__saldo = saldo # privado
def verificar_pin(self, pin_ingresado):
return self.__pin == pin_ingresado
def get_saldo(self, pin):
if not self.verificar_pin(pin):
raise PermissionError('PIN incorrecto')
return self.__saldo
def depositar(self, monto, pin):
if not self.verificar_pin(pin):
raise PermissionError('PIN incorrecto')
self.__saldo += monto
return self.__saldo
cuenta = CuentaSegura('Ana', '1234', 500)
print(cuenta.get_saldo('1234')) # 500
cuenta.depositar(200, '1234')
print(cuenta.get_saldo('1234')) # 700
try:
cuenta.get_saldo('9999')
except PermissionError as e:
print(f'Error: {e}')
Proyecto: Sistema de empleados
class Empleado:
empresa = 'TechCorp SA'
def __init__(self, nombre, cargo, salario_base):
self.nombre = nombre
self.cargo = cargo
self.salario_base = salario_base
self.bonos = []
def agregar_bono(self, monto, razon):
self.bonos.append({'monto': monto, 'razon': razon})
print(f'Bono de {monto} a {self.nombre}: {razon}')
def salario_total(self):
return self.salario_base + sum(b['monto'] for b in self.bonos)
def resumen(self):
print(f'--- {self.nombre} ---')
print(f'Cargo: {self.cargo}')
print(f'Empresa: {self.empresa}')
print(f'Salario total: {self.salario_total()}')
ana = Empleado('Ana Garcia', 'Desarrolladora Senior', 5000)
carlos = Empleado('Carlos Mendez', 'Disenador', 3500)
ana.agregar_bono(1000, 'Proyecto exitoso')
ana.agregar_bono(500, 'Horas extra')
carlos.agregar_bono(750, 'Diseno de marca')
ana.resumen()
carlos.resumen()
Revisemos los puntos más importantes de esta lección antes de continuar.
Resumen
- Una clase es una plantilla para crear objetos
- Un objeto es una instancia de una clase
- init es el constructor
- self referencia al objeto actual
- Los atributos guardan datos, los metodos definen comportamiento
- La encapsulacion (doble guion bajo) protege datos internos
En la proxima leccion: herencia y polimorfismo.
- Comprendo el concepto principal y puedo explicarlo con mis palabras
- Entiendo cómo aplicarlo en mi situación especÃfica
- Necesito repasar algunas partes antes de continuar
- Quiero ver más ejemplos prácticos del tema