Introducción
Contenido de la lección.
Objetivos de aprendizaje
- Objetivo 1
- Objetivo 2
- Objetivo 3
Objetivos de Aprendizaje
Al finalizar este tema, serás capaz de:
- Distinguir entre atributos de instancia y atributos de clase, implementando cada uno apropiadamente
- Aplicar los conceptos de encapsulación mediante atributos públicos y privados
- Implementar métodos de instancia, de clase y estáticos según las necesidades del programa
- Desarrollar aplicaciones profesionales utilizando principios de programación orientada a objetos
📚 Conceptos Fundamentales: Atributos de Clase vs Atributos de Instancia
Los atributos de clase son variables que pertenecen a la clase en sí misma y son compartidas por todas las instancias. Se definen directamente en el cuerpo de la clase, fuera de cualquier método. Por el contrario, los atributos de instancia son únicos para cada objeto creado y se definen típicamente en el método __init__. Esta distinción es crucial para el manejo eficiente de la memoria y la organización lógica del código. Los atributos de clase son útiles para datos que deben ser compartidos entre todas las instancias, como contadores globales o configuraciones predeterminadas.
class Estudiante:
# Atributo de clase
universidad = 'Universidad Nacional'
contador_estudiantes = 0
def __init__(self, nombre, carrera):
# Atributos de instancia
self.nombre = nombre
self.carrera = carrera
Estudiante.contador_estudiantes += 1
def mostrar_info(self):
return f'{self.nombre} estudia {self.carrera} en {self.universidad}'
# Creación de instancias
estudiante1 = Estudiante('Ana', 'Ingeniería')
estudiante2 = Estudiante('Carlos', 'Medicina')
print(estudiante1.mostrar_info())
print(estudiante2.mostrar_info())
print(f'Total estudiantes: {Estudiante.contador_estudiantes}')
Ana estudia Ingeniería en Universidad Nacional Carlos estudia Medicina en Universidad Nacional Total estudiantes: 2
🎯 Atributos Públicos y Privados: Encapsulación en Python
La encapsulación es un principio fundamental de la POO que permite controlar el acceso a los datos de una clase. En Python, los atributos públicos son accesibles desde cualquier parte del código, mientras que los atributos privados (precedidos por doble guión bajo __) están destinados a ser utilizados solo dentro de la clase. Los atributos protegidos (con un solo guión bajo _) son una convención que indica que el atributo es para uso interno pero técnicamente sigue siendo accesible. Esta práctica mejora la seguridad del código y facilita el mantenimiento al establecer interfaces claras.
class CuentaBancaria:
def __init__(self, titular, saldo_inicial):
self.titular = titular # Público
self._numero_cuenta = 12345 # Protegido
self.__saldo = saldo_inicial # Privado
def consultar_saldo(self):
return self.__saldo
def depositar(self, cantidad):
if cantidad > 0:
self.__saldo += cantidad
return f'Depósito exitoso. Saldo actual: ${self.__saldo}'
return 'Cantidad inválida'
def _validar_retiro(self, cantidad):
return cantidad > 0 and cantidad <= self.__saldo
cuenta = CuentaBancaria('Juan Pérez', 1000)
print(f'Titular: {cuenta.titular}')
print(cuenta.consultar_saldo())
print(cuenta.depositar(500))
# print(cuenta.__saldo) # Esto causaría un error
print(f'Número de cuenta: {cuenta._numero_cuenta}')
Titular: Juan Pérez 1000 Depósito exitoso. Saldo actual: $1500 Número de cuenta: 12345
💡 Métodos de Clase y Métodos Estáticos
Los métodos de clase (@classmethod) reciben la clase como primer parámetro (convencionalmente llamado 'cls') y pueden acceder a atributos de clase pero no a atributos de instancia. Son útiles para crear constructores alternativos o para operaciones que afectan a toda la clase. Los métodos estáticos (@staticmethod) no reciben automáticamente ningún parámetro especial y funcionan como funciones normales pero están organizados dentro de la clase por razones lógicas. Ambos tipos de métodos se pueden llamar desde la clase sin necesidad de crear una instancia.
class Producto:
iva = 0.21 # Atributo de clase
total_productos = 0
def __init__(self, nombre, precio):
self.nombre = nombre
self.precio = precio
Producto.total_productos += 1
@classmethod
def cambiar_iva(cls, nuevo_iva):
cls.iva = nuevo_iva
return f'IVA actualizado a {nuevo_iva}'
@classmethod
def crear_producto_oferta(cls, nombre, precio_original, descuento):
precio_final = precio_original * (1 - descuento)
return cls(nombre, precio_final)
@staticmethod
def es_precio_valido(precio):
return precio > 0
def precio_con_iva(self):
return self.precio * (1 + self.iva)
# Uso de métodos de clase y estáticos
print(Producto.es_precio_valido(100))
print(Producto.cambiar_iva(0.18))
producto_oferta = Producto.crear_producto_oferta('Laptop', 1000, 0.15)
print(f'{producto_oferta.nombre}: ${producto_oferta.precio}')
print(f'Total productos: {Producto.total_productos}')
True IVA actualizado a 0.18 Laptop: $850.0 Total productos: 1
🔧 Casos de Uso Avanzados: Sistema de Gestión Empresarial
En aplicaciones empresariales reales, la combinación de atributos y métodos de clase con instancia permite crear sistemas robustos y escalables. Un ejemplo típico es un sistema de gestión de empleados donde necesitamos rastrear información tanto individual como corporativa. Los atributos de clase pueden almacenar políticas empresariales, mientras que los métodos de clase pueden generar reportes o actualizar configuraciones globales. Los atributos privados protegen información sensible como salarios, y los métodos estáticos proporcionan funciones de validación que pueden usarse independientemente de las instancias.
class Empleado:
# Atributos de clase
empresa = 'TechCorp'
salario_minimo = 30000
_contador_empleados = 0
def __init__(self, nombre, departamento, salario):
self.nombre = nombre
self.departamento = departamento
self.__salario = salario
self._id_empleado = self._generar_id()
Empleado._contador_empleados += 1
@classmethod
def _generar_id(cls):
return f'EMP{cls._contador_empleados + 1:04d}'
@classmethod
def actualizar_salario_minimo(cls, nuevo_minimo):
cls.salario_minimo = nuevo_minimo
return f'Salario mínimo actualizado a ${nuevo_minimo}'
@staticmethod
def validar_departamento(departamento):
departamentos_validos = ['IT', 'RRHH', 'Ventas', 'Finanzas']
return departamento in departamentos_validos
def obtener_salario(self):
return self.__salario
def aumentar_salario(self, porcentaje):
self.__salario *= (1 + porcentaje / 100)
return f'Salario aumentado. Nuevo salario: ${self.__salario:.2f}'
# Implementación del sistema
emp1 = Empleado('María García', 'IT', 45000)
emp2 = Empleado('Pedro López', 'Ventas', 35000)
print(f'Empleado: {emp1.nombre}, ID: {emp1._id_empleado}')
print(f'¿IT es válido? {Empleado.validar_departamento("IT")}')
print(emp1.aumentar_salario(10))
print(f'Total empleados: {Empleado._contador_empleados}')
Empleado: María García, ID: EMP0001 ¿IT es válido? True Salario aumentado. Nuevo salario: $49500.00 Total empleados: 2
Ejercicios Prácticos
Sistema de Biblioteca Digital
INTERMEDIODescripción:
Crea una clase Libro con atributos de clase para el total de libros y la biblioteca. Implementa atributos privados para ISBN, métodos de clase para cambiar la biblioteca, y métodos estáticos para validar ISBN. Incluye métodos de instancia para préstamo y devolución.
Sistema de Vehículos
AVANZADODescripción:
Diseña una jerarquía de clases para un concesionario. La clase base Vehiculo debe tener atributos de clase para el concesionario y métodos para calcular impuestos. Implementa atributos privados para el precio y métodos de clase para estadísticas de ventas.
Gestión de Cursos Universitarios
BÁSICODescripción:
Implementa un sistema donde la clase Curso tenga atributos de clase para la universidad y semestre actual. Los atributos de instancia deben incluir lista privada de estudiantes inscritos. Agrega métodos de clase para cambiar semestre y métodos estáticos para validar códigos de curso.
Sistema Bancario Completo
AVANZADODescripción:
Desarrolla un sistema bancario con clases para diferentes tipos de cuentas. Implementa atributos de clase para tasas de interés, atributos privados para saldos, métodos de clase para actualizar tasas, y métodos estáticos para validar números de cuenta.
Resumen y Próximos Pasos
Los atributos y métodos de clase son herramientas poderosas que permiten crear aplicaciones Python más organizadas y eficientes. La comprensión de la encapsulación mediante atributos públicos y privados, junto con el uso apropiado de métodos de instancia, clase y estáticos, forma la base para el desarrollo de software profesional. Estos conceptos son fundamentales para avanzar hacia temas más complejos como herencia, polimorfismo y patrones de diseño. El dominio de estos principios es esencial para cualquier ingeniero de software que busque crear aplicaciones robustas y mantenibles.