¿Qué es la Programación Orientada a Objetos?
La Programación Orientada a Objetos (POO) es un paradigma de programación que organiza el código en objetos que contienen datos (atributos) y funciones (métodos). Python soporta completamente POO.
Conceptos Básicos
Clase
Una clase es un plano o plantilla para crear objetos. Define los atributos y métodos que tendrán los objetos.
Objeto
Un objeto es una instancia de una clase. Es una entidad concreta creada a partir de la plantilla de la clase.
Crear una Clase Simple
# Definir una clase
class Persona:
# Método constructor
def __init__(self, nombre, edad):
self.nombre = nombre # Atributo de instancia
self.edad = edad
# Método de instancia
def saludar(self):
return f"Hola, soy {self.nombre} y tengo {self.edad} años"
# Crear objetos (instancias)
persona1 = Persona("Ana", 25)
persona2 = Persona("Luis", 30)
# Usar métodos
print(persona1.saludar()) # Hola, soy Ana y tengo 25 años
print(persona2.saludar()) # Hola, soy Luis y tengo 30 años
# Acceder a atributos
print(persona1.nombre) # Ana
print(persona2.edad) # 30El Constructor __init__
El método __init__ es el constructor de la clase. Se ejecuta automáticamente cuando se crea un objeto.
class CuentaBancaria:
def __init__(self, titular, saldo_inicial=0):
self.titular = titular
self.saldo = saldo_inicial
print(f"Cuenta creada para {titular}")
def depositar(self, cantidad):
self.saldo += cantidad
return f"Depositado: ${cantidad}. Saldo: ${self.saldo}"
def retirar(self, cantidad):
if cantidad <= self.saldo:
self.saldo -= cantidad
return f"Retirado: ${cantidad}. Saldo: ${self.saldo}"
return "Fondos insuficientes"
def consultar_saldo(self):
return f"Saldo actual: ${self.saldo}"
# Crear cuenta
cuenta = CuentaBancaria("Ana", 1000)
print(cuenta.depositar(500)) # Depositado: $500. Saldo: $1500
print(cuenta.retirar(300)) # Retirado: $300. Saldo: $1200
print(cuenta.consultar_saldo()) # Saldo actual: $1200Atributos de Clase vs Atributos de Instancia
class Coche:
# Atributo de clase (compartido por todas las instancias)
ruedas = 4
def __init__(self, marca, modelo):
# Atributos de instancia (únicos para cada objeto)
self.marca = marca
self.modelo = modelo
coche1 = Coche("Toyota", "Corolla")
coche2 = Coche("Honda", "Civic")
print(coche1.ruedas) # 4
print(coche2.ruedas) # 4
print(coche1.marca) # Toyota
print(coche2.marca) # Honda
# Modificar atributo de clase
Coche.ruedas = 6
print(coche1.ruedas) # 6 (afecta a todos)
print(coche2.ruedas) # 6Métodos Especiales (Dunder Methods)
class Libro:
def __init__(self, titulo, autor, paginas):
self.titulo = titulo
self.autor = autor
self.paginas = paginas
# Representación en string
def __str__(self):
return f"{self.titulo} por {self.autor}"
# Representación técnica
def __repr__(self):
return f"Libro('{self.titulo}', '{self.autor}', {self.paginas})"
# Longitud
def __len__(self):
return self.paginas
# Comparación
def __eq__(self, otro):
return self.titulo == otro.titulo and self.autor == otro.autor
libro1 = Libro("Python Avanzado", "Juan Pérez", 350)
libro2 = Libro("Python Avanzado", "Juan Pérez", 350)
print(libro1) # Python Avanzado por Juan Pérez
print(repr(libro1)) # Libro('Python Avanzado', 'Juan Pérez', 350)
print(len(libro1)) # 350
print(libro1 == libro2) # TrueEncapsulamiento
Python usa convenciones para indicar atributos privados o protegidos:
class CuentaBancaria:
def __init__(self, titular, saldo_inicial):
self.titular = titular
self.__saldo = saldo_inicial # Atributo privado (__)>
# Método getter
def get_saldo(self):
return self.__saldo
# Método setter
def set_saldo(self, nuevo_saldo):
if nuevo_saldo >= 0:
self.__saldo = nuevo_saldo
else:
print("Saldo no puede ser negativo")
def depositar(self, cantidad):
if cantidad > 0:
self.__saldo += cantidad
cuenta = CuentaBancaria("Ana", 1000)
print(cuenta.get_saldo()) # 1000
# print(cuenta.__saldo) # Error: atributo privado
cuenta.depositar(500)
print(cuenta.get_saldo()) # 1500Herencia
La herencia permite crear clases basadas en otras clases, heredando sus atributos y métodos:
# Clase base (padre)
class Animal:
def __init__(self, nombre, edad):
self.nombre = nombre
self.edad = edad
def hacer_sonido(self):
return "Algún sonido"
def info(self):
return f"{self.nombre} tiene {self.edad} años"
# Clase derivada (hija)
class Perro(Animal):
def __init__(self, nombre, edad, raza):
super().__init__(nombre, edad) # Llamar constructor del padre
self.raza = raza
# Sobrescribir método
def hacer_sonido(self):
return "Guau guau"
def info(self):
return f"{super().info()} y es un {self.raza}"
class Gato(Animal):
def hacer_sonido(self):
return "Miau miau"
# Crear instancias
perro = Perro("Max", 3, "Labrador")
gato = Gato("Luna", 2)
print(perro.hacer_sonido()) # Guau guau
print(gato.hacer_sonido()) # Miau miau
print(perro.info()) # Max tiene 3 años y es un LabradorHerencia Múltiple
class Volador:
def volar(self):
return "Estoy volando"
class Nadador:
def nadar(self):
return "Estoy nadando"
class Pato(Volador, Nadador):
def __init__(self, nombre):
self.nombre = nombre
def hacer_sonido(self):
return "Cuac cuac"
pato = Pato("Donald")
print(pato.volar()) # Estoy volando
print(pato.nadar()) # Estoy nadando
print(pato.hacer_sonido()) # Cuac cuacPolimorfismo
class Forma:
def area(self):
pass
class Rectangulo(Forma):
def __init__(self, base, altura):
self.base = base
self.altura = altura
def area(self):
return self.base * self.altura
class Circulo(Forma):
def __init__(self, radio):
self.radio = radio
def area(self):
return 3.14159 * self.radio ** 2
# Polimorfismo en acción
formas = [
Rectangulo(5, 10),
Circulo(7),
Rectangulo(3, 4)
]
for forma in formas:
print(f"Área: {forma.area():.2f}")Métodos de Clase y Estáticos
class Empleado:
aumento_salarial = 1.05 # Atributo de clase
def __init__(self, nombre, salario):
self.nombre = nombre
self.salario = salario
# Método de instancia
def aplicar_aumento(self):
self.salario = int(self.salario * self.aumento_salarial)
# Método de clase
@classmethod
def cambiar_aumento(cls, nuevo_aumento):
cls.aumento_salarial = nuevo_aumento
# Método estático
@staticmethod
def es_dia_laboral(dia):
return dia.weekday() < 5
emp = Empleado("Ana", 50000)
emp.aplicar_aumento()
print(emp.salario) # 52500
Empleado.cambiar_aumento(1.10)
emp2 = Empleado("Luis", 60000)
emp2.aplicar_aumento()
print(emp2.salario) # 66000
import datetime
mi_fecha = datetime.date(2025, 11, 17)
print(Empleado.es_dia_laboral(mi_fecha)) # True o FalseEjemplo Práctico: Sistema de Biblioteca
class Libro:
def __init__(self, titulo, autor, isbn):
self.titulo = titulo
self.autor = autor
self.isbn = isbn
self.prestado = False
def prestar(self):
if not self.prestado:
self.prestado = True
return f"{self.titulo} ha sido prestado"
return f"{self.titulo} ya está prestado"
def devolver(self):
if self.prestado:
self.prestado = False
return f"{self.titulo} ha sido devuelto"
return f"{self.titulo} no estaba prestado"
class Biblioteca:
def __init__(self, nombre):
self.nombre = nombre
self.libros = []
def agregar_libro(self, libro):
self.libros.append(libro)
return f"Libro {libro.titulo} agregado"
def buscar_libro(self, titulo):
for libro in self.libros:
if libro.titulo.lower() == titulo.lower():
return libro
return None
def listar_disponibles(self):
disponibles = [libro for libro in self.libros if not libro.prestado]
return disponibles
# Usar el sistema
biblioteca = Biblioteca("Biblioteca Central")
libro1 = Libro("Python Básico", "Juan Pérez", "123")
libro2 = Libro("Python Avanzado", "María López", "456")
biblioteca.agregar_libro(libro1)
biblioteca.agregar_libro(libro2)
print(libro1.prestar()) # Python Básico ha sido prestado
print(libro1.prestar()) # Python Básico ya está prestado
disponibles = biblioteca.listar_disponibles()
for libro in disponibles:
print(f"Disponible: {libro.titulo}")Conclusión
La Programación Orientada a Objetos es un paradigma poderoso que permite organizar código de manera lógica y reutilizable. Las clases y objetos son fundamentales en Python y te ayudarán a crear aplicaciones más estructuradas y mantenibles.