make_dataclass decorator: transforme dataclasses

Publicado em: 23/10/2026
Tempo de leitura: 6 minutos
Código Python em um notebook para criar dataclasses dinâmicas

make_dataclass decorator no Python é um recurso útil para quem cria classes dinamicamente e precisa aplicar uma transformação personalizada no momento em que a classe é construída. A função dataclasses.make_dataclass() já permitia montar dataclasses em tempo de execução, mas o parâmetro decorator amplia bastante esse fluxo: em vez de obrigatoriamente finalizar a classe com @dataclass, você pode escolher uma função compatível que receba a classe recém-criada e devolva a versão transformada.

Esse detalhe é especialmente interessante em geradores de modelos, bibliotecas de serialização, sistemas de plugins, ferramentas de linha de comando, clientes de API e aplicações que convertem esquemas externos em objetos Python. Ao longo deste guia, você verá como o parâmetro funciona, como escrever decorators seguros, quais cuidados tomar com validação e compatibilidade e quando uma classe declarada normalmente continua sendo a melhor escolha.

O que é make_dataclass?

make_dataclass() cria uma nova classe a partir de um nome, uma lista de campos, classes-base e opções semelhantes às aceitas por @dataclass. Em vez de escrever a classe estaticamente, você descreve sua estrutura usando dados.

from dataclasses import make_dataclass

Produto = make_dataclass(
    "Produto",
    [
        ("nome", str),
        ("preco", float),
        ("ativo", bool, True),
    ],
)

item = Produto("Curso de Python", 199.90)
print(item)

A classe resultante recebe automaticamente métodos como __init__, __repr__ e __eq__, conforme as opções selecionadas. O comportamento geral é o mesmo de uma dataclass comum, mas a definição nasce em tempo de execução.

Para que serve o parâmetro decorator?

O parâmetro decorator define qual função será responsável por transformar a classe criada. Essa função deve aceitar a classe como primeiro argumento e também precisa lidar com as opções de dataclass repassadas por palavra-chave.

from dataclasses import dataclass, make_dataclass

def meu_decorator(cls, **kwargs):
    print(f"Decorando {cls.__name__}")
    return dataclass(cls, **kwargs)

Cliente = make_dataclass(
    "Cliente",
    [("nome", str), ("email", str)],
    decorator=meu_decorator,
)

O decorator acima apenas registra o nome da classe e delega a transformação final para dataclass(). Em um projeto real, ele poderia incluir validações, registrar a classe em um catálogo, adicionar metadados ou integrar uma biblioteca externa.

Exemplo com registro automático

Imagine um sistema que cria tipos a partir de uma especificação recebida de uma API. Você pode armazenar automaticamente cada classe em um registro central.

from dataclasses import dataclass, make_dataclass

REGISTRO = {}

def registrar_dataclass(cls, **kwargs):
    transformada = dataclass(cls, **kwargs)
    REGISTRO[transformada.__name__] = transformada
    return transformada

Pedido = make_dataclass(
    "Pedido",
    [("id", int), ("total", float), ("status", str)],
    decorator=registrar_dataclass,
    slots=True,
)

print(REGISTRO["Pedido"])

Esse padrão evita uma segunda etapa de registro e mantém a criação da classe concentrada em uma única operação. Para sistemas maiores, o registro também pode armazenar versão do esquema, origem dos campos, regras de serialização e identificadores usados por plugins.

Validação de campos antes da transformação

Um decorator também pode inspecionar as anotações da classe antes de chamar dataclass(). Isso permite rejeitar estruturas inválidas cedo.

from dataclasses import dataclass, make_dataclass

TIPOS_PERMITIDOS = {str, int, float, bool}

def validar_tipos(cls, **kwargs):
    invalidos = {
        nome: tipo
        for nome, tipo in cls.__annotations__.items()
        if tipo not in TIPOS_PERMITIDOS
    }
    if invalidos:
        raise TypeError(f"Tipos não permitidos: {invalidos}")
    return dataclass(cls, **kwargs)

Configuracao = make_dataclass(
    "Configuracao",
    [("host", str), ("porta", int), ("debug", bool)],
    decorator=validar_tipos,
)

Essa abordagem é útil quando a classe será construída com dados externos. Mesmo assim, não confunda validação da estrutura com validação dos valores em tempo de execução. Anotações de tipo não impedem que alguém passe um valor incompatível ao construtor; para isso, você ainda precisa de __post_init__, descriptors, bibliotecas de validação ou checagem explícita.

Integração com __post_init__

Você pode fornecer métodos no namespace da classe usando o argumento namespace.

from dataclasses import make_dataclass

def validar(self):
    if self.preco < 0:
        raise ValueError("O preço não pode ser negativo")

def apos_init(self):
    validar(self)

Produto = make_dataclass(
    "Produto",
    [("nome", str), ("preco", float)],
    namespace={"__post_init__": apos_init, "validar": validar},
)

O decorator personalizado pode complementar esse namespace, mas deve evitar sobrescrever métodos fornecidos pelo chamador sem uma regra clara. Em bibliotecas públicas, conflitos silenciosos tornam o comportamento difícil de depurar.

Cuidados com os argumentos recebidos

O decorator deve aceitar **kwargs porque make_dataclass() pode encaminhar opções como init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, slots e weakref_slot. Ignorar ou bloquear essas opções pode produzir erros inesperados.

from dataclasses import dataclass

def decorator_seguro(cls, **kwargs):
    # faça inspeções ou registros aqui
    return dataclass(cls, **kwargs)

Evite também chamar dataclass() duas vezes sobre a mesma classe sem necessidade. Embora algumas combinações possam parecer funcionar, decorar repetidamente pode gerar resultados difíceis de manter, especialmente com slots, herança ou métodos já existentes.

Compatibilidade e fallback

Antes de usar o parâmetro em uma biblioteca distribuída, defina claramente a versão mínima do Python. Em ambientes antigos, o argumento pode não existir. Um fallback razoável é criar a classe normalmente e depois aplicar o decorator manualmente.

from dataclasses import make_dataclass

campos = [("nome", str), ("valor", int)]

try:
    Registro = make_dataclass(
        "Registro",
        campos,
        decorator=registrar_dataclass,
    )
except TypeError:
    Registro = make_dataclass("Registro", campos)
    REGISTRO[Registro.__name__] = Registro

Em código de produção, prefira verificar sys.version_info ou a assinatura disponível, em vez de capturar qualquer TypeError, pois um erro desse tipo também pode ser causado por outra parte da configuração.

Quando usar e quando evitar

Use make_dataclass() quando os campos realmente dependem de dados carregados em tempo de execução. Exemplos incluem esquemas JSON, tabelas descobertas dinamicamente, definições de formulários, plugins e modelos gerados por configuração. O parâmetro decorator faz sentido quando todas essas classes precisam passar pela mesma transformação adicional.

Evite geração dinâmica quando a estrutura é conhecida durante o desenvolvimento. Uma classe escrita normalmente oferece melhor navegação no editor, mensagens de erro mais claras, documentação mais simples e suporte superior de ferramentas estáticas. Se o objetivo é apenas reduzir repetição em poucas classes, uma função auxiliar ou uma classe-base pode ser mais previsível.

Testes recomendados

Teste a assinatura do construtor, os valores padrão, o comportamento com frozen=True, a criação com slots=True, a herança, o registro e as exceções produzidas pelo decorator. Também confirme que o objeto resultante continua compatível com dataclasses.fields(), asdict() e replace().

from dataclasses import fields, is_dataclass

assert is_dataclass(Pedido)
assert [campo.name for campo in fields(Pedido)] == ["id", "total", "status"]
assert REGISTRO["Pedido"] is Pedido

Boas práticas

Mantenha o decorator pequeno, determinístico e documentado. Não faça acesso à rede, gravação em banco ou outras operações lentas durante a criação da classe. Valide nomes e tipos vindos de fontes externas, não use eval() para converter anotações e evite registrar classes com nomes conflitantes sem uma política de versão.

Para aprofundar conceitos relacionados, veja também nossos guias sobre cópias de objetos no Python, introspecção com inspect, UUIDs no Python e ordenação de dependências com graphlib.

Como referências externas, consulte a documentação oficial de dataclasses e a PEP 557, que descreve a motivação e o modelo original das dataclasses.

Conclusão

O parâmetro decorator transforma make_dataclass() em um ponto de extensão mais flexível. Ele permite validar, registrar e adaptar classes dinâmicas sem separar a criação da transformação. A melhor implementação é simples: receba a classe, trate as opções de forma transparente, aplique a transformação necessária e devolva o tipo final. Com testes e limites claros, o recurso pode reduzir bastante a complexidade de geradores de modelos e sistemas baseados em esquemas.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Estrutura de código e XML para canonicalização com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ElementTree canonicalize: normalize XML no Python

    Aprenda ElementTree canonicalize no Python para comparar XML, gerar hashes estáveis e controlar namespaces, espaços e comentários.

    Ler mais

    Tempo de leitura: 5 minutos
    22/10/2026
    Código de programação Python em uma tela
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    T-strings e templatelib: interpolação estruturada

    Aprenda t-strings e string.templatelib no Python para processar interpolações, criar renderizadores seguros e preservar dados estruturados.

    Ler mais

    Tempo de leitura: 6 minutos
    22/10/2026
    Banco SQLite e código Python para exportação seletiva com iterdump
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SQLite iterdump: crie dumps seletivos no Python

    Aprenda sqlite3.Connection.iterdump com filtro para gerar dumps seletivos, validar dependências e restaurar bancos com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    21/10/2026
    Código e arquivos representando compressão de dados no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard rápido no Python

    Aprenda a usar compression.zstd no Python para comprimir e descomprimir dados com desempenho, streaming e segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    21/10/2026
    Arquivo de configuração e estrutura de código para ConfigParser no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ConfigParser: leia INI sem seção no Python

    Aprenda a ler arquivos INI sem cabeçalho usando ConfigParser, com validação, tipos, compatibilidade e escrita segura.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Dados binários e memória de computador representando memoryview no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview count e index: busque sem copiar

    Aprenda memoryview count e index no Python para buscar valores em buffers sem cópias e validar dados binários com eficiência.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026