Módulo 3 - Dataclasses e estruturas de dados
@dataclass: menos código, mais intenção
9 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Declarar uma dataclass com campos anotados por tipo.
- Entender o __init__, o __repr__ e o __eq__ que o decorador gera.
- Comparar uma dataclass com a mesma classe escrita à mão.
- Definir valores padrão para campos de forma correta.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: @dataclass: menos código, mais intenção.
Os objetivos desta aula. Declarar uma dataclass com campos anotados por tipo. Entender o __init__, o __repr__ e o __eq__ que o decorador gera. Comparar uma dataclass com a mesma classe escrita à mão. Definir valores padrão para campos de forma correta.
Veja o essencial, parte por parte.
O que o decorador gera para você. @dataclass gera o __init__, o __repr__ e o __eq__ a partir dos campos.
A mesma classe, à mão e com dataclass. Para sentir a diferença, vale ver a versão manual.
Valores padrão nos campos. Campos podem ter valor padrão, escrito com o sinal de igual depois do tipo.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
O que o decorador gera para você
Uma classe cujo papel é só guardar dados costuma render muito código repetitivo: um __init__ que recebe cada atributo e o atribui a self, um __repr__ para imprimir o objeto de forma legível, e um __eq__ para comparar dois objetos por conteúdo. O decorador @dataclass, da biblioteca padrão, gera tudo isso a partir dos campos que você declara. Você escreve apenas o nome e o tipo de cada campo, e o Python monta os métodos por baixo dos panos.
from dataclasses import dataclass
@dataclass
class Ponto:
x: float
y: float
p = Ponto(3.0, 4.0)
print(p) # Ponto(x=3.0, y=4.0) <- __repr__ gerado
print(p == Ponto(3.0, 4.0)) # True <- __eq__ gerado
print(p.x, p.y) # 3.0 4.0Só os campos x e y foram declarados; o __init__, o __repr__ e o __eq__ vieram de graça.
Repare no que você não escreveu: nenhum __init__, nenhum __repr__, nenhum __eq__. Mesmo assim, Ponto(3.0, 4.0) constrói o objeto, print mostra Ponto(x=3.0, y=4.0) em vez de um endereço de memória, e a comparação por igualdade funciona campo a campo. Tudo isso saiu dos dois campos anotados. É menos código para escrever, menos para errar e menos para manter.
A mesma classe, à mão e com dataclass
Para sentir a diferença, vale ver a versão manual. Sem dataclass, a classe Ponto exigiria escrever o construtor atribuindo cada parâmetro, mais um __repr__ que monta o texto à mão, mais um __eq__ que compara os campos e ainda checa o tipo do outro objeto. São dezenas de linhas para algo que o decorador resolve em duas. E quanto mais campos, maior a economia e menor a chance de esquecer um deles em algum dos métodos.
# Versao manual, sem dataclass:
class PontoManual:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"PontoManual(x={self.x}, y={self.y})"
def __eq__(self, outro):
if not isinstance(outro, PontoManual):
return NotImplemented
return self.x == outro.x and self.y == outro.y
# Versao com dataclass faz o mesmo em duas linhas de campo.A versão manual repete o que a dataclass gera sozinha a partir dos campos.
Classe à mão
- Escrever __init__ atribuindo cada campo
- Escrever __repr__ montando o texto
- Escrever __eq__ comparando campo a campo
- Risco de esquecer um campo em algum método
Com @dataclass
- Declarar cada campo como nome: tipo
- __repr__ gerado automaticamente
- __eq__ gerado, comparando todos os campos
- Adicionar um campo atualiza tudo de uma vez
Valores padrão nos campos
Campos podem ter valor padrão, escrito com o sinal de igual depois do tipo. Um campo com padrão pode ser omitido na construção. Assim como em funções, os campos com padrão precisam vir depois dos campos sem padrão, porque a ordem vira a ordem dos argumentos do __init__ gerado. Isso cobre o caso simples de valores imutáveis como números, textos e booleanos. Para valores mutáveis, como listas, há uma regra especial que a próxima aula detalha.
from dataclasses import dataclass
@dataclass
class Produto:
nome: str
preco: float
em_estoque: bool = True
desconto: float = 0.0
print(Produto("Caneca", 25.0))
# Produto(nome='Caneca', preco=25.0, em_estoque=True, desconto=0.0)
print(Produto("Livro", 40.0, False, 0.1))
# Produto(nome='Livro', preco=40.0, em_estoque=False, desconto=0.1)Campos com padrão (em_estoque, desconto) podem ser omitidos e vêm depois dos sem padrão.
No exemplo, nome e preco são obrigatórios; em_estoque e desconto têm padrão e podem ficar de fora. O __init__ gerado respeita essa ordem. Com isso você já modela a maioria das classes de dados simples do dia a dia. As próximas aulas acrescentam as ferramentas para casos mais exigentes: campos com valores mutáveis, imutabilidade e validação na construção.
Teste rápido
O que o decorador @dataclass gera automaticamente a partir dos campos declarados?
Perguntas frequentes
- Preciso importar algo para usar @dataclass?
- Sim, do módulo dataclasses da biblioteca padrão: from dataclasses import dataclass. Ele já vem com o Python, sem instalar nada. O mesmo módulo traz outras ferramentas que você vê no módulo, como field e a função para gerar dicionários a partir da instância.
- A dataclass funciona no Playground do curso?
- Funciona. Dataclasses fazem parte da biblioteca padrão desde o Python 3.7, então rodam no Playground sem qualquer preparação. Você pode colar os exemplos deste módulo e experimentar à vontade, incluindo os campos com padrão e a comparação por igualdade.
- Posso adicionar métodos comuns a uma dataclass?
- Pode, e é normal. A dataclass gera os métodos de dados para você, mas continua sendo uma classe: você adiciona métodos próprios no corpo dela normalmente. Um método que calcula a distância de um Ponto à origem, por exemplo, convive tranquilo com os campos e os métodos gerados.
- O __repr__ gerado serve para exibir ao usuário final?
- Ele é ótimo para depuração e logs, mostrando a classe e os valores de forma clara. Para uma mensagem voltada ao usuário final, você ainda pode escrever um __str__ próprio com o texto amigável. O __repr__ prioriza ser preciso e informativo para quem programa.
- Dataclass deixa o código mais lento?
- Não de forma perceptível. Os métodos são gerados uma vez, quando a classe é definida, e depois se comportam como métodos escritos à mão. Em uso normal, a diferença de desempenho para uma classe manual é desprezível, e você ganha muito em clareza e manutenção.
- Por que os campos com valor padrão precisam vir por último?
- Porque a ordem dos campos vira a ordem dos parâmetros do __init__ gerado, e em Python um parâmetro com valor padrão não pode preceder um sem padrão. Colocar os campos com padrão no fim evita esse conflito e faz o construtor funcionar como esperado.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.