Dicionários aninhados e estruturas complexas
1. Fundamentos dos Dicionários Aninhados
Dicionários aninhados são dicionários que contêm outros dicionários como valores, formando uma estrutura hierárquica de dados. Essa técnica é fundamental em Python para representar informações com múltiplos níveis de profundidade, como dados JSON, configurações de sistemas e estruturas de dados hierárquicas.
# Sintaxe básica de um dicionário aninhado
usuario = {
"nome": "Ana Silva",
"contato": {
"email": "ana@exemplo.com",
"telefone": {
"residencial": "11-3333-4444",
"celular": "11-99999-8888"
}
},
"endereco": {
"rua": "Av. Paulista",
"numero": 1000,
"cidade": "São Paulo"
}
}
# Acessando valores aninhados
print(usuario["contato"]["email"]) # ana@exemplo.com
print(usuario["contato"]["telefone"]["celular"]) # 11-99999-8888
Casos de uso comuns incluem representação de dados de APIs REST, configurações de aplicações, estruturas de árvore e dados geográficos.
2. Construção e Manipulação de Estruturas Aninhadas
Podemos construir dicionários aninhados manualmente ou dinamicamente com loops.
# Construção manual
config = {
"banco": {
"host": "localhost",
"porta": 5432,
"credenciais": {
"usuario": "admin",
"senha": "secret"
}
}
}
# Construção com loop
alunos = {}
nomes = ["João", "Maria", "Pedro"]
notas = [[8, 7, 9], [10, 9, 8], [6, 7, 8]]
for i, nome in enumerate(nomes):
alunos[nome] = {
"matricula": i + 1,
"notas": notas[i],
"media": sum(notas[i]) / len(notas[i])
}
# Adicionando novo nível dinamicamente
alunos["João"]["endereco"] = {"cidade": "Rio", "estado": "RJ"}
# Removendo nível
del alunos["João"]["endereco"]["estado"]
# Atualizando valores em profundidade
alunos["Maria"]["notas"].append(9)
alunos["Maria"]["media"] = sum(alunos["Maria"]["notas"]) / len(alunos["Maria"]["notas"])
# Usando update() para mesclar
novo_dado = {"João": {"status": "ativo"}}
alunos.update(novo_dado)
3. Navegação e Acesso a Dados Profundos
O acesso encadeado é a forma mais direta, mas requer cuidado com chaves inexistentes.
dados_empresa = {
"departamentos": {
"TI": {
"gerente": "Carlos",
"funcionarios": ["Ana", "Bob", "Carol"],
"orcamento": 500000
},
"RH": {
"gerente": "Diana",
"funcionarios": ["Eve", "Frank"],
"orcamento": 200000
}
}
}
# Acesso encadeado
print(dados_empresa["departamentos"]["TI"]["gerente"]) # Carlos
# Tratamento com try/except
try:
orcamento = dados_empresa["departamentos"]["Marketing"]["orcamento"]
except KeyError:
orcamento = 0
print("Departamento não encontrado")
# Uso de get() para navegação segura
orcamento_marketing = dados_empresa.get("departamentos", {}).get("Marketing", {}).get("orcamento", 0)
print(f"Orçamento Marketing: {orcamento_marketing}") # 0
# Função auxiliar para acesso profundo
def acessar_aninhado(dicionario, *chaves, padrao=None):
resultado = dicionario
for chave in chaves:
try:
resultado = resultado[chave]
except (KeyError, TypeError):
return padrao
return resultado
print(acessar_aninhado(dados_empresa, "departamentos", "TI", "orcamento")) # 500000
print(acessar_aninhado(dados_empresa, "departamentos", "Vendas", "orcamento", padrao=0)) # 0
4. Iteração em Estruturas Aninhadas
Para percorrer estruturas aninhadas, precisamos de loops aninhados ou recursão.
# Iteração em dois níveis
catalogo = {
"eletronicos": {
"smartphones": 10,
"laptops": 5,
"tablets": 3
},
"roupas": {
"camisetas": 20,
"calcas": 15,
"sapatos": 8
}
}
for categoria, itens in catalogo.items():
print(f"\nCategoria: {categoria}")
for item, quantidade in itens.items():
print(f" {item}: {quantidade} unidades")
# Iteração recursiva para profundidade arbitrária
def iterar_aninhado(dicionario, prefixo=""):
for chave, valor in dicionario.items():
caminho = f"{prefixo}.{chave}" if prefixo else chave
if isinstance(valor, dict):
iterar_aninhado(valor, caminho)
else:
print(f"{caminho}: {valor}")
# Função geradora para extrair todos os pares chave-valor
def extrair_pares(dicionario, prefixo=""):
for chave, valor in dicionario.items():
caminho = f"{prefixo}.{chave}" if prefixo else chave
if isinstance(valor, dict):
yield from extrair_pares(valor, caminho)
else:
yield (caminho, valor)
print("\n--- Iteração recursiva ---")
iterar_aninhado(catalogo)
print("\n--- Pares extraídos ---")
for caminho, valor in extrair_pares(catalogo):
print(f"{caminho} = {valor}")
5. Combinação com Listas e Outras Coleções
Estruturas complexas frequentemente combinam dicionários com listas e outras coleções.
# Lista de dicionários (estrutura tabular)
funcionarios = [
{"id": 1, "nome": "Ana", "cargo": "Analista", "salario": 5000},
{"id": 2, "nome": "Bob", "cargo": "Programador", "salario": 6000},
{"id": 3, "nome": "Carol", "cargo": "Gerente", "salario": 8000}
]
# Dicionário com listas como valores (agrupamento)
por_cargo = {}
for func in funcionarios:
cargo = func["cargo"]
if cargo not in por_cargo:
por_cargo[cargo] = []
por_cargo[cargo].append(func["nome"])
print("Funcionários por cargo:", por_cargo)
# Misturando tuplas, sets e dicionários
dados_complexos = {
"projeto": "Sistema X",
"equipe": {
"desenvolvedores": [("Ana", "Python"), ("Bob", "Java")],
"testadores": {"Carlos", "Diana"} # set
},
"versoes": {
1.0: {"data": "2024-01", "features": ["login", "cadastro"]},
2.0: {"data": "2024-06", "features": ["relatorios", "dashboard"]}
}
}
# Acessando estruturas mistas
print(dados_complexos["equipe"]["desenvolvedores"][0]) # ('Ana', 'Python')
print(dados_complexos["versoes"][2.0]["features"]) # ['relatorios', 'dashboard']
6. Técnicas Avançadas de Manipulação
Técnicas como mesclagem profunda, achatamento e transformação são essenciais para manipular estruturas complexas.
from copy import deepcopy
# Mesclagem profunda (deep merge)
def deep_merge(dict1, dict2):
resultado = deepcopy(dict1)
for chave, valor in dict2.items():
if chave in resultado and isinstance(resultado[chave], dict) and isinstance(valor, dict):
resultado[chave] = deep_merge(resultado[chave], valor)
else:
resultado[chave] = deepcopy(valor)
return resultado
config_padrao = {"banco": {"host": "localhost", "porta": 5432}}
config_usuario = {"banco": {"host": "192.168.1.100"}, "debug": True}
config_final = deep_merge(config_padrao, config_usuario)
print("Config mesclada:", config_final)
# Achatamento (flatten) de dicionário aninhado
def flatten_dict(dicionario, separador="_", prefixo=""):
itens = []
for chave, valor in dicionario.items():
novo_prefixo = f"{prefixo}{separador}{chave}" if prefixo else chave
if isinstance(valor, dict):
itens.extend(flatten_dict(valor, separador, novo_prefixo).items())
else:
itens.append((novo_prefixo, valor))
return dict(itens)
dados_aninhados = {"a": {"b": {"c": 1, "d": 2}, "e": 3}}
dados_planos = flatten_dict(dados_aninhados)
print("Dados achatados:", dados_planos) # {'a_b_c': 1, 'a_b_d': 2, 'a_e': 3}
# Transformação com compreensão de dicionários
quadrados = {x: x**2 for x in range(5)}
print("Quadrados:", quadrados) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# Transformação aninhada com compreensão
matriz = {(i, j): i * j for i in range(3) for j in range(3)}
print("Matriz:", matriz)
7. Padrões e Boas Práticas
Ao trabalhar com dicionários aninhados, algumas práticas ajudam a manter o código limpo e eficiente.
# Quando usar dicionários aninhados vs. classes
# Use dicionários para dados simples e temporários
# Use classes para comportamentos e validações
from dataclasses import dataclass
@dataclass
class Endereco:
rua: str
cidade: str
cep: str
@dataclass
class Usuario:
nome: str
endereco: Endereco
# Alternativa com dicionário (mais flexível, menos seguro)
usuario_dict = {
"nome": "Ana",
"endereco": {"rua": "Av. Brasil", "cidade": "SP", "cep": "01001-000"}
}
# Evitando aninhamento excessivo
# Ruim: config["database"]["connection"]["pool"]["size"]
# Melhor: criar objetos separados para cada nível
# Serialização com JSON
import json
dados = {"nome": "João", "idade": 30, "endereco": {"cidade": "RJ"}}
with open("dados.json", "w") as f:
json.dump(dados, f, indent=2)
with open("dados.json", "r") as f:
dados_carregados = json.load(f)
print("Dados carregados do JSON:", dados_carregados)
Referências
- Documentação Oficial do Python: Dicionários — Guia completo sobre dicionários em Python, incluindo métodos e operações básicas.
- Real Python: Dicionários Aninhados em Python — Tutorial detalhado sobre criação, acesso e manipulação de dicionários aninhados.
- GeeksforGeeks: Python Nested Dictionary — Exemplos práticos de dicionários aninhados com diferentes abordagens de implementação.
- Python Documentation: json module — Documentação oficial do módulo JSON para serialização e desserialização de estruturas aninhadas.
- Stack Overflow: How to iterate through nested dictionaries — Discussão sobre técnicas de iteração em dicionários aninhados com soluções recursivas.
- Programiz: Python Nested Dictionary — Tutorial com exemplos de criação, acesso e atualização de dicionários aninhados.