Dataclasses en Python
🗺️ Mapa de la Lección
⏱ 45 minutos🟡 Intermedio🎯 Qué aprenderás
- Entender qué son las dataclasses y cuándo usarlas
- Crear dataclasses con @dataclass
- Usar campos con valores por defecto
- Aplicar frozen para inmutabilidad
- Comparar objetos automáticamente
🌍 Para qué te sirve
- Clases que principalmente almacenan datos (estructuras de datos)
- Configuraciones: settings, opciones de aplicación
- Resultados de funciones: devolver múltiples valores estructurados
- DTOs (Data Transfer Objects): transferir datos entre capas
- Modelos simples: cuando no necesitas lógica compleja
- Comparación automática: cuando necesitas __eq__ automático
🎯 ¿Por qué aprender dataclasses?
Imagina que necesitas una clase solo para guardar datos (como un usuario, una configuración, o un producto). Con clases normales escribirías mucho código repetitivo para __init__, __repr__, __eq__, etc.
Las dataclasses te permiten:
- Escribir menos código: Python genera métodos automáticamente
- Código más legible: sintaxis más clara y concisa
- Comparación automática:
__eq__generado automáticamente - Representación automática:
__repr__útil por defecto - Menos errores: menos código manual = menos bugs
🌍 Casos reales donde se usa
Las dataclasses son perfectas para clases que principalmente almacenan datos:
- Configuraciones: Settings, opciones de aplicación
- Resultados de funciones: Devolver múltiples valores estructurados
- DTOs (Data Transfer Objects): Transferir datos entre capas
- Modelos simples: Cuando no necesitas lógica compleja
- Estructuras de datos: Clases que principalmente almacenan información
Ejemplo real: Un sistema de configuración usa dataclasses para representar opciones: @dataclass class Config: host: str, port: int, debug: bool. Python genera automáticamente todos los métodos necesarios.
💡 Concepto base
Las dataclasses son una forma simplificada de crear clases que principalmente almacenan datos. Con un simple decorador @dataclass, Python genera automáticamente __init__, __repr__, __eq__ y más.
Lo genial de Python: Puedes crear clases de datos con mucho menos código que las clases tradicionales.
from dataclasses import dataclass
# Con dataclass (simple y claro)
@dataclass
class Persona:
nombre: str
edad: int
email: str
# Crear instancia
persona = Persona("Ana", 25, "ana@email.com")
print(persona) # Representación automática
Persona(nombre='Ana', edad=25, email='ana@email.com')
Las dataclasses son como una plantilla predefinida para crear chilaquiles al pastor. En lugar de escribir cada vez todos los pasos (cortar tortillas, freír, agregar salsa, queso, pastor), defines una vez la estructura básica y Python automáticamente te da todos los métodos necesarios. Es como tener un molde que ya sabe cómo crear el plato completo, solo necesitas especificar los ingredientes y el molde hace el resto del trabajo. Mucho más rápido y menos propenso a errores.
Antes de continuar: Asegúrate de entender Clases y Objetos y Type Hints.
¿Cuándo usar dataclasses?
- Clases que principalmente almacenan datos
- Cuando necesitas comparación automática entre objetos
- Configuraciones y estructuras simples
¿Cuándo NO usar dataclasses?
- Clases con mucha lógica de negocio (usa clases normales)
- Cuando necesitas control total sobre
__init__o métodos especiales
Antes de continuar: Asegúrate de entender Clases y Type Hints.
Conceptos Básicos
Creación Básica
Con el decorador @dataclass, Python genera automáticamente los métodos necesarios:
from dataclasses import dataclass
@dataclass
class Persona:
nombre: str
edad: int
email: str
¿Qué hace @dataclass automáticamente?
- Genera
__init__()con los parámetros - Genera
__repr__()para mostrar el objeto - Genera
__eq__()para comparar objetos - Y más métodos útiles
Uso:
# Crear objeto - igual que una clase normal
persona1 = Persona("Ana", 28, "ana@email.com")
persona2 = Persona("Ana", 28, "ana@email.com")
print(persona1) # Persona(nombre='Ana', edad=28, email='ana@email.com')
print(persona1 == persona2) # True (comparación automática)
Comparación con clase normal: Esto es equivalente a ~20 líneas de código en una clase normal.
Con Valores por Defecto
Puedes dar valores por defecto a los atributos:
@dataclass
class Configuracion:
host: str = "localhost" # Valor por defecto
puerto: int = 8080 # Valor por defecto
debug: bool = False # Valor por defecto
Uso:
# Usar todos los valores por defecto
config1 = Configuracion() # host="localhost", puerto=8080, debug=False
# Sobrescribir algunos
config2 = Configuracion(host="192.168.1.1", puerto=3000)
# host="192.168.1.1", puerto=3000, debug=False (usa el default)
# Especificar todos
config3 = Configuracion(host="prod.server.com", puerto=443, debug=True)
Importante: Los atributos con valores por defecto deben ir después de los que no tienen. Python requiere esto.
Características Avanzadas
Inmutabilidad
@dataclass(frozen=True)
class Punto:
x: float
y: float
Comparación Personalizada
@dataclass(order=True)
class Producto:
nombre: str
precio: float
stock: int
Ejemplos de Uso
Validación de Datos
@dataclass
class ErrorValidacion:
fila: int
campo: str
valor: str
mensaje: str
# Uso
error = ErrorValidacion(
fila=1,
campo="email",
valor="invalido",
mensaje="Formato de email incorrecto"
)
Configuración
@dataclass
class ConfiguracionDB:
host: str
puerto: int
usuario: str
password: str
base_datos: str
# Uso
config = ConfiguracionDB(
host="localhost",
puerto=5432,
usuario="admin",
password="secret",
base_datos="mi_db"
)
Métodos Especiales
Post Init
@dataclass
class Rectangulo:
ancho: float
alto: float
area: float = None
def __post_init__(self):
self.area = self.ancho * self.alto
Métodos Personalizados
@dataclass
class Producto:
nombre: str
precio: float
stock: int
def tiene_stock(self) -> bool:
return self.stock > 0
def valor_total(self) -> float:
return self.precio * self.stock
Buenas Prácticas
- Usa Type Hints
@dataclass
class Usuario:
id: int
nombre: str
activo: bool
- Documenta tus Clases
@dataclass
class Transaccion:
"""Representa una transacción financiera."""
id: int
monto: float
fecha: str
- Considera la Inmutabilidad
@dataclass(frozen=True)
class Constantes:
PI: float = 3.14159
GRAVEDAD: float = 9.81
Ejemplo Práctico
from dataclasses import dataclass
from typing import List, Optional
from datetime import datetime
@dataclass
class Orden:
id: int
cliente: str
productos: List[str]
total: float
fecha: datetime
estado: str = "pendiente"
notas: Optional[str] = None
def __post_init__(self):
if self.total < 0:
raise ValueError("El total no puede ser negativo")
def esta_completada(self) -> bool:
return self.estado == "completada"
def agregar_producto(self, producto: str, precio: float):
self.productos.append(producto)
self.total += precio
Consejos
- Usa dataclasses para estructuras de datos simples
- Aprovecha los type hints para mejor documentación
- Considera usar
frozen=Truepara datos inmutables - Usa
__post_init__para validaciones y cálculos
Recursos Adicionales
Documentación Oficial
- Documentación oficial de dataclasses
- PEP 557 - Data Classes
- Type Hints en Python
- attrs Documentation - Alternativa a dataclasses
Bibliografía Recomendada
- Fluent Python (Luciano Ramalho) - Capítulo 5: Data Class Builders
- Effective Python (Brett Slatkin) - Item 37: Use Comprehensions Instead of map and filter
- Python Tricks (Dan Bader) - Capítulo sobre dataclasses
- Python Cookbook, 3rd Ed (Beazley & Jones) - Recetas sobre dataclasses
Conceptos Relacionados
- Clases y Objetos - Base para entender dataclasses
- Type Hints - Usa type hints con dataclasses
- Diccionarios y Sets - Dataclasses organizan datos
Siguiente paso
Ahora que conoces las dataclasses, aprende a manejar errores de manera profesional. Continúa con: Manejo de Excepciones