---
title: "Dataclasses em Python: Guia Completo com Exemplos Práticos"
url: "https://python.dev.br/blog/dataclasses-python-guia-completo/"
markdown_url: "https://python.dev.br/blog/dataclasses-python-guia-completo.MD"
description: "Aprenda a usar dataclasses em Python para reduzir boilerplate. Guia completo com exemplos práticos de frozen, slots, herança, field() e comparação com Pydantic."
date: "2026-03-27"
author: "Equipe python.dev.br"
---

# Dataclasses em Python: Guia Completo com Exemplos Práticos

Aprenda a usar dataclasses em Python para reduzir boilerplate. Guia completo com exemplos práticos de frozen, slots, herança, field() e comparação com Pydantic.


Se você já criou classes em Python apenas para armazenar dados, sabe o quanto de código repetitivo isso envolve: `__init__`, `__repr__`, `__eq__`... tudo escrito manualmente. As **dataclasses**, introduzidas no Python 3.7, resolvem exatamente esse problema, gerando automaticamente esses métodos a partir de simples anotações de tipo.

## O Que São Dataclasses

Dataclasses são classes Python decoradas com `@dataclass` que geram automaticamente métodos especiais como `__init__`, `__repr__`, `__eq__` e outros. A ideia é simples: você declara os atributos com [type hints](/blog/tipagem-estatica-python-mypy/) e o Python cuida do resto.

Veja a diferença entre uma classe tradicional e uma dataclass:

```python
# Classe tradicional — muito boilerplate
class ProdutoTradicional:
    def __init__(self, nome: str, preco: float, estoque: int = 0):
        self.nome = nome
        self.preco = preco
        self.estoque = estoque

    def __repr__(self):
        return f"ProdutoTradicional(nome={self.nome!r}, preco={self.preco}, estoque={self.estoque})"

    def __eq__(self, other):
        if not isinstance(other, ProdutoTradicional):
            return NotImplemented
        return (self.nome, self.preco, self.estoque) == (other.nome, other.preco, other.estoque)
```

```python
# Com dataclass — limpo e direto
from dataclasses import dataclass

@dataclass
class Produto:
    nome: str
    preco: float
    estoque: int = 0
```

As duas versões produzem o mesmo resultado, mas a dataclass tem **três linhas** em vez de treze. Essa redução de boilerplate é o principal motivo para adotar dataclasses no seu código Python.

## Valores Padrão e a Função field()

Valores padrão simples funcionam como em qualquer classe. Porém, para tipos mutáveis (listas, dicionários, sets), você precisa usar `field()` com `default_factory` para evitar o clássico problema de objetos mutáveis compartilhados:

```python
from dataclasses import dataclass, field

@dataclass
class Pedido:
    cliente: str
    itens: list[str] = field(default_factory=list)
    metadata: dict[str, str] = field(default_factory=dict)
    _total: float = field(init=False, default=0.0)  # não aparece no __init__
```

Os parâmetros mais úteis de `field()` são:

- `default_factory` — função chamada para gerar o valor padrão
- `init=False` — exclui o campo do `__init__`
- `repr=False` — exclui o campo do `__repr__`
- `compare=False` — exclui o campo das comparações (`__eq__`)

## Validação com \_\_post\_init\_\_

Dataclasses não fazem validação automática de dados. Para adicionar lógica de validação, use o método `__post_init__`, que é chamado logo após o `__init__` gerado:

```python
from dataclasses import dataclass

@dataclass
class ContaBancaria:
    titular: str
    saldo: float
    limite: float = 1000.0

    def __post_init__(self):
        if not self.titular.strip():
            raise ValueError("Titular não pode estar vazio")
        if self.saldo < -self.limite:
            raise ValueError(f"Saldo {self.saldo} excede o limite de {self.limite}")
        self.titular = self.titular.strip().title()

# Funciona
conta = ContaBancaria("  joão silva  ", 500.0)
print(conta.titular)  # "João Silva"

# Levanta ValueError
try:
    conta_invalida = ContaBancaria("", 100.0)
except ValueError as e:
    print(e)  # "Titular não pode estar vazio"
```

Se você precisa de validação robusta e automática baseada em tipos, o [Pydantic](/blog/pydantic-validacao-dados-python/) é a ferramenta certa. A regra geral: use dataclasses para estruturas internas e Pydantic quando os dados vêm de fontes externas (APIs, formulários, arquivos).

## Dataclasses Imutáveis com frozen

Ao passar `frozen=True`, a dataclass se torna imutável — qualquer tentativa de alterar um atributo levanta um `FrozenInstanceError`:

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class Coordenada:
    latitude: float
    longitude: float

ponto = Coordenada(-23.5505, -46.6333)
print(ponto)  # Coordenada(latitude=-23.5505, longitude=-46.6333)

# Isso levanta FrozenInstanceError
# ponto.latitude = 0.0
```

Dataclasses frozen também são automaticamente hasháveis, o que permite usá-las como chaves de dicionário ou em conjuntos. Isso é útil para modelar objetos de valor (value objects) no seu domínio.

## Otimização de Memória com slots

A partir do Python 3.10, você pode usar `slots=True` para gerar `__slots__` automaticamente, economizando memória e ganhando velocidade no acesso a atributos:

```python
from dataclasses import dataclass

@dataclass(slots=True)
class Sensor:
    id: int
    tipo: str
    valor: float
    ativo: bool = True
```

Com `slots=True`, o Python não cria um `__dict__` por instância. Em aplicações com milhares de objetos, como leitura de dados de sensores IoT ou [processamento de dados com Pandas](/blog/introducao-ao-pandas/), essa economia de memória pode ser significativa.

## Parâmetros Keyword-Only com kw\_only

Também a partir do Python 3.10, `kw_only=True` força todos os campos a serem passados como argumentos nomeados:

```python
from dataclasses import dataclass

@dataclass(kw_only=True)
class ConfiguracaoDB:
    host: str
    porta: int = 5432
    usuario: str
    senha: str
    banco: str

# Obrigatório usar nomes — mais legível e menos sujeito a erros
config = ConfiguracaoDB(
    host="localhost",
    usuario="admin",
    senha="secreta",
    banco="minha_app"
)
```

Esse recurso é excelente para classes de configuração onde a ordem dos parâmetros não é óbvia. Compare com a abordagem de [gerenciar configurações com Pydantic Settings](/blog/pydantic-validacao-dados-python/) para projetos maiores.

## Herança com Dataclasses

Dataclasses suportam herança normalmente. A classe filha herda todos os campos da classe mãe:

```python
from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class Evento:
    nome: str
    data: datetime

@dataclass
class EventoOnline(Evento):
    url: str
    plataforma: str = "Zoom"
    participantes: list[str] = field(default_factory=list)

evento = EventoOnline(
    nome="Python Brasil 2026",
    data=datetime(2026, 10, 15),
    url="https://pythonbrasil.org.br/live"
)
print(evento)
```

Uma regra importante: campos com valor padrão devem vir depois de campos sem valor padrão. Isso pode causar problemas em herança se a classe mãe tem campos com default e a filha adiciona campos obrigatórios. Use `kw_only=True` para contornar isso. Para entender melhor herança em Python, confira nosso guia de [programação orientada a objetos](/blog/programacao-orientada-objetos-python/).

## Utilitários: asdict e astuple

As funções `asdict()` e `astuple()` convertem dataclasses para dicionários e tuplas, respectivamente. Muito útil para serialização:

```python
from dataclasses import dataclass, asdict, astuple

@dataclass
class Usuario:
    nome: str
    email: str
    idade: int

user = Usuario("Maria", "maria@email.com", 28)

# Para dicionário — útil para JSON
dados = asdict(user)
print(dados)  # {'nome': 'Maria', 'email': 'maria@email.com', 'idade': 28}

# Para tupla — útil para banco de dados
registro = astuple(user)
print(registro)  # ('Maria', 'maria@email.com', 28)
```

Isso se integra bem com bibliotecas de banco de dados como [SQLite](/blog/python-e-banco-de-dados-sqlite/) e [PostgreSQL](/blog/python-e-postgresql/), onde você pode converter dataclasses para inserções SQL facilmente.

## Dataclass vs NamedTuple vs Pydantic

Quando usar cada uma?

| Característica | `dataclass` | `NamedTuple` | `Pydantic` |
|---|---|---|---|
| Mutável | Sim (padrão) | Não | Sim (padrão) |
| Validação automática | Não | Não | Sim |
| Performance | Rápido | Muito rápido | Rápido (v2 com Rust) |
| Serialização JSON | Via `asdict()` | Via `_asdict()` | Nativo |
| Uso ideal | Domínio interno | Dados imutáveis simples | Dados externos/APIs |

Use **dataclasses** para modelar objetos do domínio da sua aplicação. Use **Pydantic** quando precisar validar dados de fontes externas. Use **NamedTuple** para tuplas imutáveis e leves.

## Exemplo Prático: Modelando um Sistema de Pedidos

Vamos juntar tudo em um exemplo mais completo:

```python
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum

class StatusPedido(Enum):
    PENDENTE = "pendente"
    PAGO = "pago"
    ENVIADO = "enviado"
    ENTREGUE = "entregue"

@dataclass
class ItemPedido:
    produto: str
    quantidade: int
    preco_unitario: float

    @property
    def subtotal(self) -> float:
        return self.quantidade * self.preco_unitario

@dataclass
class Pedido:
    cliente: str
    itens: list[ItemPedido] = field(default_factory=list)
    status: StatusPedido = StatusPedido.PENDENTE
    criado_em: datetime = field(default_factory=datetime.now)

    def __post_init__(self):
        if not self.cliente.strip():
            raise ValueError("Cliente é obrigatório")

    @property
    def total(self) -> float:
        return sum(item.subtotal for item in self.itens)

    def adicionar_item(self, produto: str, quantidade: int, preco: float):
        self.itens.append(ItemPedido(produto, quantidade, preco))

# Uso
pedido = Pedido(cliente="Ana Costa")
pedido.adicionar_item("Teclado Mecânico", 1, 350.00)
pedido.adicionar_item("Mouse Gamer", 2, 150.00)

print(f"Total: R$ {pedido.total:.2f}")  # Total: R$ 650.00
print(f"Status: {pedido.status.value}")  # Status: pendente
```

Esse padrão de modelagem com dataclasses é muito usado em projetos que seguem [boas práticas Python](/blog/boas-praticas-python-2026/) e [design patterns](/blog/design-patterns-python/).

## Considerações Finais

Dataclasses são uma das ferramentas mais úteis do Python moderno. Elas eliminam boilerplate sem sacrificar clareza, integram-se perfeitamente com type hints e oferecem opções avançadas como `frozen`, `slots` e `kw_only`.

Se você está começando, use dataclasses como substituto padrão para classes simples de dados. Para validação mais robusta, combine com [tratamento de erros adequado](/blog/tratamento-de-erros-python/) ou migre para Pydantic. E para entender melhor como context managers podem ajudar a gerenciar recursos nos seus objetos, confira nosso artigo sobre [context managers em Python](/blog/context-managers-python-with/).

> **Data classes em outras linguagens**: <a href="https://kotlin.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Kotlin</a> popularizou o conceito de data classes com `data class`, que gera automaticamente `equals()`, `hashCode()`, `toString()` e `copy()` — inspiração direta para as dataclasses do Python. Já <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a> usa structs com `#[derive(Debug, Clone, PartialEq)]` para funcionalidade similar.
