dataclass_transform en Python: clases generadas

Publicado el: 29/08/2026
Tempo de leitura: 5 minutos
Close-up view of a computer screen displaying code in a software development environment.

typing.dataclass_transform permite que una biblioteca informe al analizador estático que un decorator, una clase base o una metaclase ofrece comportamiento similar a @dataclass. Frameworks que generan __init__, campos, igualdad, ordenación o modelos declarativos pueden ofrecer tipado preciso sin exigir el decorator estándar.

El marcador no transforma clases por sí mismo. La biblioteca debe implementar el runtime. Esta guía cubre decorators, bases, metaclases, defaults, field specifiers, alias, campos keyword-only, frozen, herencia, pruebas estáticas y alineación con runtime.

El problema de las clases generadas

def modelo(cls):
    # genera __init__ dinámicamente
    return cls

@modelo
class Usuario:
    nombre: str
    edad: int

usuario = Usuario(nombre="Ana", edad=30)

El framework puede crear el constructor en runtime. Sin información adicional, el analizador ve una clase sin un __init__ compatible y rechaza la llamada.

Marcar el decorator

from typing import dataclass_transform

@dataclass_transform()
def modelo(cls):
    return transformar_en_modelo(cls)

El analizador trata clases decoradas con @modelo como dataclass-like y puede sintetizar el constructor a partir de las anotaciones.

Runtime sigue siendo responsabilidad de la biblioteca

dataclass_transform no genera métodos. Si transformar_en_modelo() no crea __init__, el código falla aunque el análisis estático sea correcto. El contrato y la implementación deben mantenerse alineados.

Tipar el decorator

from typing import TypeVar

T = TypeVar("T", bound=type)

@dataclass_transform()
def modelo(cls: T) -> T:
    return transformar_en_modelo(cls)

El decorator devuelve la misma clase estáticamente. APIs complejas pueden necesitar overloads o genéricos adicionales.

Clase base transformadora

@dataclass_transform()
class ModeloBase:
    pass

class Producto(ModeloBase):
    id: int
    nombre: str

producto = Producto(id=1, nombre="Teclado")

Todas las subclases se tratan como dataclass-like. Es común en ORMs, validación y configuración.

Metaclase transformadora

@dataclass_transform()
class ModeloMeta(type):
    ...

class Modelo(metaclass=ModeloMeta):
    ...

Una metaclase puede recopilar anotaciones, crear descriptors, generar métodos y registrar campos.

eq_default

@dataclass_transform(eq_default=True)
def modelo(cls):
    ...

Indica si se generan métodos de igualdad por defecto.

order_default

@dataclass_transform(order_default=False)
def modelo(cls):
    ...

Describe si se generan métodos de ordenación. El runtime debe coincidir.

kw_only_default

@dataclass_transform(kw_only_default=True)
class ModeloBase:
    ...

Los campos se tratan como keyword-only:

class Usuario(ModeloBase):
    nombre: str
    edad: int

Usuario(nombre="Ana", edad=30)
# Usuario("Ana", 30) debería rechazarse

frozen_default

Las formas modernas pueden indicar que los modelos son congelados por defecto. Esto afecta asignaciones y herencia. Si se promete frozen, el runtime debe impedir mutación.

Field specifiers

def campo(*, default=..., alias: str | None = None):
    ...

@dataclass_transform(field_specifiers=(campo,))
def modelo(cls):
    ...

El analizador puede interpretar defaults, factories, alias, inclusión en init y flags keyword-only.

Default y default_factory

class Carrito(ModeloBase):
    items: list[str] = campo(default_factory=list)
    abierto: bool = campo(default=True)

La biblioteca debe evitar defaults mutables compartidos y ejecutar factories por instancia.

Campos fuera de __init__

class Registro(ModeloBase):
    id: int = campo(init=False)
    nombre: str

Si el specifier comunica init=False, el analizador omite el campo del constructor. El runtime debe rellenarlo de otra forma.

Alias de parámetros

class Usuario(ModeloBase):
    nombre_completo: str = campo(alias="nombre")

Algunos frameworks aceptan Usuario(nombre="Ana") aunque el atributo sea nombre_completo. Los field specifiers pueden comunicarlo.

Orden de campos

Los campos sin default suelen ir antes de los campos con default en constructores posicionales. Keyword-only puede flexibilizar la regla. La firma estática debe coincidir con runtime.

Herencia

class Entidad(ModeloBase):
    id: int

class Usuario(Entidad):
    nombre: str

El analizador combina campos de base y derivada. Orden, defaults y frozen deben respetar el contrato.

Override de campos

Una subclase puede cambiar tipo, default u opciones. Define políticas claras. Overrides incompatibles rompen constructores y sustitución.

Herencia frozen

Modelos frozen y no frozen requieren reglas consistentes. No prometas inmutabilidad estática si el runtime permite mutación.

Decorators con argumentos

@dataclass_transform()
def modelo(*, frozen: bool = False, kw_only: bool = False):
    def aplicar(cls):
        return transformar(cls, frozen=frozen, kw_only=kw_only)
    return aplicar

@modelo(frozen=True)
class Configuracion:
    host: str

Nombres y valores deben seguir patrones entendidos por analizadores. Overloads con Literal[True] pueden mejorar precisión.

Paréntesis opcionales

Un decorator usable como @modelo y @modelo(...) puede necesitar overloads. Prueba ambas formas. APIs excesivamente dinámicas son difíciles de modelar.

Comparación con @dataclass

Usa @dataclass directamente cuando sea suficiente. dataclass_transform está dirigido a autores de frameworks con validación, ORM, conversión, descriptors o semánticas propias.

Comparación con Protocol

Protocol describe capacidades existentes. dataclass_transform informa que una herramienta sintetiza métodos y constructores. Resuelven problemas diferentes.

Introspección

El marcador puede exponer __dataclass_transform__. Eso no convierte las clases en dataclasses reales y dataclasses.is_dataclass() puede devolver falso.

Generación de schemas

El framework debe mantener su propio modelo de campos, defaults, validación y schemas. dataclass_transform mejora el tipado, pero no añade serialización.

Pruebas estáticas

from typing import assert_type

usuario = Usuario(nombre="Ana", edad=30)
assert_type(usuario.nombre, str)
assert_type(usuario.edad, int)

Añade casos que deben fallar: campos ausentes, nombres extra, tipos incorrectos, posiciones prohibidas y mutación frozen.

Pruebas de runtime

Ejecuta la misma matriz. Es peligroso que el analizador acepte una llamada que falla. Prueba inspect.signature(), creación, defaults, factories, igualdad y herencia.

Compatibilidad de analizadores

Mypy y pyright pueden implementar detalles en momentos diferentes. Sigue la especificación y ejecuta suites en todas las herramientas soportadas.

Compatibilidad de Python

Usa typing_extensions.dataclass_transform en versiones anteriores. El import no cambia la lógica de transformación.

Errores comunes

  • Suponer que el marcador genera métodos: la biblioteca debe implementarlos.
  • Prometer defaults distintos: la firma diverge del runtime.
  • Olvidar field_specifiers: la función de campo queda opaca.
  • No probar herencia: orden y frozen pueden romperse.
  • Crear un decorator demasiado dinámico: los analizadores no lo modelan.
  • Usarlo cuando @dataclass basta: aumenta mantenimiento.

Ejemplo completo: mini framework

from dataclasses import dataclass, field
from typing import dataclass_transform


def atributo(*, default=..., default_factory=...):
    if default_factory is not ...:
        return field(default_factory=default_factory)
    if default is not ...:
        return field(default=default)
    return field()

@dataclass_transform(field_specifiers=(atributo,))
def modelo(cls=None, *, frozen: bool = False):
    def aplicar(objetivo):
        return dataclass(objetivo, frozen=frozen)
    if cls is None:
        return aplicar
    return aplicar(cls)

@modelo(frozen=True)
class Producto:
    id: int
    nombre: str
    tags: list[str] = atributo(default_factory=list)

producto = Producto(id=1, nombre="Teclado")

El runtime delega en una dataclass real y el marcador comunica la semántica del decorator personalizado. Frameworks reales pueden añadir validación, alias y conversión.

Cuándo usarlo

Usa dataclass_transform al crear una biblioteca que sintetiza constructores y campos desde anotaciones. Los desarrolladores de aplicaciones rara vez lo necesitan directamente. Para modelos normales, dataclasses, attrs, Pydantic o clases comunes suelen bastar.

Conclusión

dataclass_transform conecta frameworks declarativos y analizadores estáticos. Describe métodos sintetizados, parámetros, defaults, fields, keyword-only y frozen sin imponer una implementación.

La documentación oficial de dataclass_transform en Python define los parámetros. Mantén runtime y contrato alineados, declara field specifiers y protege la experiencia con pruebas estáticas y de ejecución.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TypeVarTuple en Python: genéricos variádicos

    Aprende TypeVarTuple en Python para conservar tuplas heterogéneas, modelar dimensiones y crear genéricos con parámetros variables.

    Ler mais

    Tempo de leitura: 4 minutos
    29/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    assert_type y reveal_type: prueba inferencia de tipos

    Aprende assert_type y reveal_type en Python para inspeccionar inferencia, probar APIs tipadas y evitar regresiones estáticas.

    Ler mais

    Tempo de leitura: 4 minutos
    29/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    get_type_hints en Python: lee anotaciones

    Aprende get_type_hints en Python para resolver referencias futuras, conservar Annotated e inspeccionar funciones y clases con seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    runtime_checkable en Python: Protocol runtime

    Aprende runtime_checkable en Python para comprobar Protocol con isinstance, entender límites y diseñar contratos estructurales seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Unpack en Python: kwargs y tipos variádicos

    Aprende typing.Unpack en Python para tipar **kwargs con TypedDict, expandir tuplas variádicas y conservar firmas precisas.

    Ler mais

    Tempo de leitura: 4 minutos
    29/08/2026
    Person holding Python logo sticker with blurred background, highlighting programming focus.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Required y NotRequired: campos opcionales en TypedDict

    Aprende Required y NotRequired en Python para controlar claves obligatorias y opcionales de TypedDict sin confundir ausencia con None.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026