Dataclasses en Python: clases de datos más limpias

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Uso de dataclasses para simplificar classes em Python

Las dataclasses reducen el código repetitivo de clases cuyo propósito principal es almacenar datos. Con el decorador @dataclass, Python puede generar automáticamente métodos como __init__, __repr__ y __eq__ a partir de anotaciones de tipo. Esto mejora la legibilidad y evita errores al mantener constructores manuales con muchos atributos.

Una dataclass sigue siendo una clase normal: puede incluir métodos, propiedades, herencia y validación. Para comprender la base, consulta la guía de programación orientada a objetos en Python.

Crear la primera dataclass

from dataclasses import dataclass

@dataclass
class Producto:
    nombre: str
    precio: float
    stock: int

producto = Producto("Teclado", 49.90, 10)

print(producto)
print(producto.nombre)

El decorador genera un constructor con los tres campos, una representación legible y comparación por valores. Las anotaciones describen la intención, pero Python no valida automáticamente los tipos en tiempo de ejecución.

Valores predeterminados

@dataclass
class Usuario:
    nombre: str
    activo: bool = True
    intentos: int = 0

Los campos sin valor predeterminado deben aparecer antes de los campos con valor predeterminado. Esta regla evita una firma de función inválida.

Evitar valores mutables compartidos

No utilices una lista literal como valor predeterminado. Emplea default_factory:

from dataclasses import dataclass, field

@dataclass
class Pedido:
    cliente: str
    productos: list[str] = field(default_factory=list)

Cada instancia recibe una lista nueva. Este patrón evita que dos pedidos compartan accidentalmente la misma colección.

Personalizar campos con field

field() permite excluir un atributo de la representación, comparación o constructor:

@dataclass
class Cuenta:
    usuario: str
    token: str = field(repr=False)
    id_interno: int = field(init=False, default=0)

repr=False evita mostrar el token en la representación, aunque no sustituye controles de seguridad. init=False impide que el campo sea un parámetro del constructor.

Validar con __post_init__

__post_init__() se ejecuta después del constructor generado:

@dataclass
class Producto:
    nombre: str
    precio: float
    stock: int = 0

    def __post_init__(self) -> None:
        if not self.nombre.strip():
            raise ValueError("El nombre es obligatorio")
        if self.precio < 0:
            raise ValueError("El precio no puede ser negativo")
        if self.stock < 0:
            raise ValueError("El stock no puede ser negativo")

Utiliza este método para invariantes sencillas y conversiones controladas. Si la validación requiere servicios externos o muchas reglas, considera una capa específica.

Campos calculados

@dataclass
class Rectangulo:
    ancho: float
    alto: float
    area: float = field(init=False)

    def __post_init__(self) -> None:
        self.area = self.ancho * self.alto

Si el área debe actualizarse cuando cambian ancho o alto, una propiedad calculada puede ser mejor que almacenar el resultado.

Dataclasses inmutables

@dataclass(frozen=True)
class Punto:
    x: float
    y: float

frozen=True impide la asignación normal después de crear la instancia. Es una protección útil contra cambios accidentales, pero no convierte automáticamente en inmutables los objetos mutables contenidos.

Ordenación de instancias

@dataclass(order=True)
class Tarea:
    prioridad: int
    titulo: str

order=True genera comparaciones siguiendo el orden de los campos. Si solo deseas ordenar por prioridad y no por título, excluye el segundo campo:

@dataclass(order=True)
class Tarea:
    prioridad: int
    titulo: str = field(compare=False)

Campos que son solo argumentos

InitVar permite recibir un valor en el constructor sin guardarlo como campo:

from dataclasses import InitVar, dataclass

@dataclass
class Perfil:
    nombre: str
    clave_temporal: InitVar[str]

    def __post_init__(self, clave_temporal: str) -> None:
        if len(clave_temporal) < 8:
            raise ValueError("Clave temporal demasiado corta")

Evita utilizar este patrón para conservar secretos en memoria sin necesidad.

Convertir a diccionario o tupla

from dataclasses import asdict, astuple

datos = asdict(producto)
valores = astuple(producto)

asdict() convierte dataclasses anidadas de forma recursiva y puede copiar estructuras. Para objetos grandes o serialización controlada, construye explícitamente el esquema esperado.

Reemplazar campos sin modificar el original

from dataclasses import replace

actualizado = replace(producto, precio=44.90)

replace() crea una nueva instancia y vuelve a ejecutar la inicialización correspondiente. Es especialmente útil con dataclasses congeladas.

Herencia

@dataclass
class Persona:
    nombre: str

@dataclass
class Empleado(Persona):
    departamento: str
    salario: float

Los campos heredados participan en el constructor. Debes respetar el orden entre campos obligatorios y predeterminados a través de la jerarquía. Evita árboles complejos cuando la composición sea más clara.

slots para reducir memoria

@dataclass(slots=True)
class Coordenada:
    latitud: float
    longitud: float

slots=True puede reducir memoria y evitar atributos arbitrarios. Revisa la compatibilidad con herencia, serialización y bibliotecas que esperan __dict__.

Dataclass frente a namedtuple y diccionario

Un diccionario es flexible, pero no declara un esquema claro. Una namedtuple es ligera e inmutable, pero ofrece menos posibilidades de validación y valores predeterminados. Una dataclass funciona bien para entidades internas, parámetros y resultados estructurados. Para estudiar anotaciones, revisa type hints en Python.

Dataclass frente a modelos de validación

Las anotaciones no convierten automáticamente strings en números ni validan datos JSON. Cuando los datos provienen de usuarios o APIs, una biblioteca de validación puede ser más adecuada. Las dataclasses son excelentes para representar datos ya confiables dentro de la aplicación.

Añadir métodos y documentación

@dataclass
class Producto:
    # Representa un artículo disponible en inventario.
    nombre: str
    precio: float

    def aplicar_descuento(self, porcentaje: float) -> float:
        return self.precio * (1 - porcentaje / 100)

La guía de docstrings en Python ayuda a documentar responsabilidades y parámetros. El decorador de dataclass también se relaciona con la guía de decoradores en Python.

Enums y estados

Cuando un campo acepta un conjunto cerrado de estados, utiliza un Enum en lugar de strings libres. La guía de Enums en Python muestra cómo evitar valores mágicos.

Fuentes oficiales

La documentación oficial de dataclasses describe todos los parámetros del decorador y de field(). El PEP 557 explica las motivaciones y decisiones de diseño originales.

Errores frecuentes

Los problemas más comunes son usar listas como valores predeterminados, creer que los tipos se validan solos, exponer secretos mediante repr, almacenar campos calculados que quedan obsoletos y activar order=True sin pensar en el orden semántico. También debes evaluar si una clase con mucha lógica sigue siendo principalmente una clase de datos.

Conclusión

Las dataclasses eliminan repetición sin quitar el poder de las clases. Declara campos con tipos, utiliza default_factory para mutables, valida invariantes en __post_init__ y elige frozen, order o slots solo cuando aporten valor. Su mayor beneficio no es ahorrar líneas, sino hacer explícita la estructura de los datos y mantener coherentes la creación, comparación y representación de objetos.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Criação de hashes seguros para senhas usando Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Hash de contraseñas con bcrypt en Python

    Aprende bcrypt en Python para almacenar contraseñas: salt, coste, hash, verificación, rehash, límites, errores y recomendaciones de seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Problemas de travamento com threading em scripts Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Threading en Python: evita que tus scripts se bloqueen

    Aprende threading en Python: hilos, tareas de entrada y salida, join, locks, colas, ThreadPoolExecutor, errores y límites del GIL.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Dicas para otimizar scripts Python lentos
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Por qué tu script Python es lento y cómo mejorarlo

    Descubre por qué un script Python es lento y cómo mejorarlo con cProfile, timeit, estructuras correctas, generadores y mejores algoritmos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Leitura de variáveis de ambiente em projetos Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Variables de entorno en Python: guía segura

    Aprende a usar variables de entorno en Python, archivos .env, validación, conversión de tipos, secretos, pruebas y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Introdução ao uso de type hints em Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Type hints en Python: escribe código más claro

    Aprende type hints en Python con funciones, colecciones, Optional, TypedDict, Callable, genéricos, Protocol y herramientas de análisis.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Medição de tempo de execução de código Python com timeit
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Timeit en Python: medir el rendimiento correctamente

    Aprende timeit en Python para comparar funciones, repetir benchmarks, separar setup, medir por operación y evitar errores de microoptimización.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026