Escolhendo a Estrutura de Dados Certa
Escolha contêineres pela forma como você lê, atualiza e percorre os dados - não por hábito. A estrutura errada custa clareza e desempenho assintótico; a correta torna o código mais curto e rápido sem micro-otimizações.
Receita
Faça quatro perguntas antes de codificar:
- Preciso de associação rápida por valor ou chave?
- A ordem deve ser preservada ou classificável?
- Duplicatas são permitidas?
- Os dados são tabulares (milhões de linhas, operações de coluna)?
Mapeie as respostas para dict, set, list, deque, heapq, ou bibliotecas de dataframe.
Quando usar isto:
- Projetando o modelo em memória de um novo módulo
- Refatorando listas aninhadas em estruturas mais claras
- Revisões de desempenho mostrando varreduras O(n) em caminhos críticos
- Discussões de arquitetura antes da escolha de persistência
Exemplo de Trabalho
from collections import defaultdict, deque
import heapq
# Padrão: indexar usuários por id -> dict
users_by_id: dict[int, dict] = {1: {"name": "Ada"}, 2: {"name": "Linus"}}
# Padrão: deduplicar tags -> set
tags = set(["py", "ml", "py"])
# Padrão: fila de trabalhos FIFO -> deque
jobs: deque[str] = deque(["a", "b"])
# Padrão: agendamento por prioridade -> heapq
heap: list[tuple[int, str]] = []
heapq.heappush(heap, (2, "low"))
heapq.heappush(heap, (1, "high"))
# Padrão: agrupar linhas -> defaultdict
by_role: defaultdict[str, list[str]] = defaultdict(list)
for name, role in [("Ada", "admin"), ("Linus", "dev")]:
by_role[role].append(name)O que isto demonstra:
- Dict para consulta por chave
- Set para exclusividade e associação
- Deque para FIFO sem penalidade de
pop(0) - Heap para ordenação por prioridade
- defaultdict para agrupar sem código repetitivo
Mergulho Profundo
Tabela de Decisão
| Padrão de acesso | Preferir | Por quê |
|---|---|---|
| Consulta por chave única | dict | O(1) em média |
| Teste de associação | set | O(1) em média |
| Adição ordenada + varredura | list | Simples, amigável ao cache |
| Fila/pilha em ambas as extremidades | deque | Pops O(1) |
| Top-K / agendamento | heapq | Atualizações O(log n) |
| Análise colunar | pandas/polars | Operações vetorizadas |
Como Funciona
- Assintóticos importam em escala - Varreduras O(n) prejudicam em 10^6 itens, ok em 50.
- Constantes importam em n pequeno - O overhead do Dict pode perder para a varredura de lista para coleções minúsculas.
- Imutabilidade sinaliza intenção - Tuplas/frozensets documentam estabilidade e permitem hashing.
- Composição supera "tamanho único" - Grafo como
dict[node, set[neighbor]]combina dict + set. - Limite de persistência - A escolha em memória difere da decisão SQL/Redis.
Notas de Python
# cheiro: busca linear repetida
if item in big_list: # O(n) a cada vez
# correção: construir set uma vez
seen = set(big_list)
if item in seen: # O(1) em médiaArmadilhas
- list como set -
if x in itemsem lista grande em loop → O(n²). Correção: Pré-construirset(items). - dict quando list de pares é suficiente - Registros de dois itens às vezes são melhores como tuplas em lista. Correção: Simplificar até que a consulta por chave seja necessária.
- pandas para 20 linhas - Importação pesada e overhead de API. Correção: dict/list simples até centenas de linhas ou operações de coluna.
- OrderedDict por padrão - Desnecessário, pois dict é ordenado. Correção: dict simples, a menos que
move_to_end. - Heap prematuro - Cinco itens ordenados uma vez →
sorted. Correção: heap quando k << n ou streaming.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| SQLite em memória | Consultas relacionais em dados moderados | Contador simples |
| Redis | Cache compartilhado entre processos | Script de processo único |
| Modelos ORM | Entidades de domínio persistentes | Buffer algorítmico efêmero |
Classes slots | Muitos objetos pequenos de campo fixo | Blobs JSON dinâmicos |
FAQs
list ou deque para pilha?
list.append/pop é bom para uso exclusivo como pilha. Use deque ao remover da esquerda ou de ambas as extremidades.
Quando pandas em vez de dict de listas?
Estatísticas por coluna, junções, filtragem em milhões de linhas - não para um dict de configuração de 30 chaves.
tupla ou dataclass?
Tupla/NamedTuple para pequenos pacotes imutáveis. dataclass quando padrões, métodos ou mutabilidade são necessários.
chave dict: str ou int?
Escolha a chave natural do domínio. IDs inteiros são mais rápidos e menores que IDs stringificados quando numéricos.
Como modelar um grafo?
dict[node, list[neighbor]] ou defaultdict(set) para grafos esparsos. NetworkX para biblioteca de algoritmos.
Counter vs dict para contagens?
Counter ao fazer matemática de multiconjunto ou most_common. Dict simples para contagem de passagem única.
bisect ou heap?
bisect para inserções majoritariamente ordenadas; heap para extração dinâmica de min/max.
Quando imutabilidade?
Chaves de dict, elementos de set, caches hash, configuração compartilhada somente leitura - tupla/frozenset/bytes.
A ordem importa para set?
Nenhuma ordem semântica - se a ordem de exibição importa, use list ou o padrão OrderedDict com dict.fromkeys.
Como documentar a escolha?
Comentário de uma linha ou ADR quando não óbvio - leitores futuros herdam o contexto.
Relacionados
- Noções Básicas de Estruturas de Dados - Visão geral Big-O
- Módulo collections - contêineres especializados
- Imutabilidade e Hashability - restrições chave
- heapq e Filas de Prioridade - agendamento
Versões da Stack: Esta página foi escrita para Python 3.14.0 (3.14 estável, 3.13 de manutenção), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.