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 rechazarsefrozen_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: strSi 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: strEl 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: strNombres 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.







