Módulo 16 - Projeto final: mini biblioteca de utilidades
Dataclasses tipadas e Enum do domínio
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Modelar um Produto com dataclass totalmente tipada.
- Criar uma Enum Categoria para valores fixos do domínio.
- Usar frozen e __post_init__ para imutabilidade e validação.
- Entender por que dataclass e Enum deixam o domínio mais seguro.
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: Dataclasses tipadas e Enum do domínio.
Os objetivos desta aula. Modelar um Produto com dataclass totalmente tipada. Criar uma Enum Categoria para valores fixos do domínio. Usar frozen e __post_init__ para imutabilidade e validação. Entender por que dataclass e Enum deixam o domínio mais seguro.
Veja o essencial, parte por parte.
A Enum do domínio. A Enum Categoria fixa as categorias válidas, evitando texto solto.
A dataclass Produto. Com a categoria pronta, modelamos o produto.
Validação com __post_init__. A dataclass gera o construtor, mas não sabe as regras do seu domínio: que o nome não pode ser vazio e que o preço não pode ser negativo.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
A Enum do domínio
Começamos pelos valores fixos do domínio. Um produto pertence a uma categoria, e o conjunto de categorias é conhecido e limitado. Representar isso com texto solto, como a palavra bebida escrita à mão em cada lugar, é um convite a erros de digitação: basta escrever bebidas no plural em um ponto e a comparação falha em silêncio. A Enum resolve isso: ela define um conjunto nomeado de valores válidos. Usar Categoria.BEBIDA é seguro, o editor completa, e qualquer valor fora da lista é um erro imediato, não um bug escondido.
# src/catalogo/modelos.py
from enum import Enum
class Categoria(Enum):
BEBIDA = "bebida"
ALIMENTO = "alimento"
LIMPEZA = "limpeza"
HIGIENE = "higiene"
# Uso seguro: o editor completa e valores invalidos falham na hora
c = Categoria.BEBIDA
print(c.value) # bebida
print(Categoria("alimento")) # Categoria.ALIMENTOA Enum Categoria fixa as opções válidas; cada membro tem um nome e um valor legível.
Repare que cada membro da Enum tem um nome, em maiúsculas, e um valor associado, aqui um texto minúsculo. O nome é o que você usa no código, Categoria.BEBIDA, claro e à prova de digitação. O valor é útil para exibir, salvar em arquivo ou reconstruir a categoria a partir de um texto vindo de fora, com Categoria(alimento). Essa dupla, nome estável para o código e valor legível para o mundo externo, é o que torna a Enum a ferramenta certa para representar conjuntos fechados de opções no domínio.
A dataclass Produto
Com a categoria pronta, modelamos o produto. Uma dataclass é perfeita: você declara os campos com seus tipos e o decorador gera o construtor, a representação legível e a comparação por valor, tudo sem escrever esse código repetitivo. O Produto tem nome, preço e categoria, cada um com seu tipo. Como preço aqui é dinheiro, usamos o tipo Decimal, que evita os erros de arredondamento do ponto flutuante em contas de dinheiro. E marcamos a classe como frozen, ou seja, imutável: uma vez criado, o produto não muda, o que evita uma classe inteira de bugs de alteração acidental.
# src/catalogo/modelos.py (continuacao)
from dataclasses import dataclass
from decimal import Decimal
@dataclass(frozen=True)
class Produto:
nome: str
preco: Decimal
categoria: Categoria
def __post_init__(self):
if not self.nome.strip():
raise ValueError("nome do produto nao pode ser vazio")
if self.preco < 0:
raise ValueError("preco nao pode ser negativo")
cafe = Produto("Cafe", Decimal("18.90"), Categoria.BEBIDA)
print(cafe) # Produto(nome="Cafe", preco=Decimal("18.90"), categoria=Categoria.BEBIDA)A dataclass Produto: campos tipados, imutável com frozen, validada no __post_init__.
Sem dataclass e Enum
- Escrever __init__ e __repr__ na mão
- Categoria como texto solto, sujeito a erro de digitação
- Comparação por identidade, não por valor
- Validação espalhada por quem cria o objeto
Com dataclass e Enum
- Construtor e representação gerados sozinhos
- Categoria com valores fixos e verificáveis
- Comparação por valor, de graça
- Validação central no __post_init__
Validação com __post_init__
A dataclass gera o construtor, mas não sabe as regras do seu domínio: que o nome não pode ser vazio e que o preço não pode ser negativo. É aí que entra o método especial __post_init__, chamado automaticamente logo depois que a dataclass monta o objeto. Ele é o lugar certo para validar. Se algo estiver errado, ele levanta um erro na hora da criação, garantindo que nenhum Produto inválido consiga existir. Essa é a ideia de tornar estados inválidos irrepresentáveis: se o objeto foi criado, ele é válido, e o resto do código pode confiar nisso sem reverificar.
Combinando as três decisões, o modelo fica robusto por construção. A tipagem torna o contrato explícito e verificável pelo mypy. O frozen impede alterações acidentais, o que também deixa o produto seguro para usar como chave de dicionário ou em um set. E o __post_init__ centraliza a validação, de modo que a garantia acontece num lugar só, não espalhada por quem cria o objeto. Nas próximas aulas, a lógica de preço vai receber esses produtos confiando que eles são válidos, porque o modelo já garantiu isso. É a fundação de tudo que vem depois.
Teste rápido
Para que serve o método __post_init__ numa dataclass?
Perguntas frequentes
- Por que usar Decimal em vez de float para preço?
- Porque float representa números em binário e sofre erros de arredondamento em contas decimais, o que é inaceitável para dinheiro. O Decimal representa os valores com precisão decimal exata, ideal para preços e valores financeiros. Em domínios de dinheiro, prefira sempre Decimal ao float.
- O que o frozen=True traz para a dataclass?
- Ele torna as instâncias imutáveis: depois de criadas, seus campos não podem ser alterados. Isso evita bugs de mudança acidental, deixa o objeto seguro para usar como chave de dicionário ou em um set, e torna o código mais fácil de raciocinar, porque o valor não muda pelas suas costas.
- Qual a vantagem da Enum sobre usar textos para categoria?
- A Enum fixa o conjunto de valores válidos e dá nomes estáveis a eles. Isso elimina erros de digitação, deixa o editor completar as opções e faz qualquer valor fora da lista falhar imediatamente. Texto solto, ao contrário, aceita qualquer coisa e esconde erros de escrita como bugs silenciosos.
- A dataclass gera comparação entre objetos?
- Sim. Por padrão, ela gera o método de igualdade comparando os campos, então dois produtos com o mesmo nome, preço e categoria são considerados iguais. Isso é comparação por valor, muito mais útil no dia a dia que a comparação por identidade padrão de objetos comuns.
- Posso ter campos opcionais numa dataclass?
- Pode. Basta dar um valor padrão ao campo, como estoque igual a zero. Campos com padrão devem vir depois dos sem padrão na declaração. Para valores padrão que são listas ou dicionários, use field com default_factory, para não compartilhar o mesmo objeto entre instâncias.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.