Programación Orientada a Objetos en Python: Clases y Objetos

👤 Admin 📅 10 de noviembre, 2025 ⏱ 18 min 🏷 Python Intermedio

¿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)    # 30

El 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: $1200

Atributos 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)  # 6

Mé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)  # True

Encapsulamiento

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())  # 1500

Herencia

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 Labrador

Herencia 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 cuac

Polimorfismo

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 False

Ejemplo 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.