Pular para o conteúdo principal

Aula 12: Combinando Estruturas de Dados


Objetivos de Aprendizagem

Ao final desta aula, você será capaz de:

  1. Criar listas de dicionários para representar coleções de registros
  2. Criar dicionários com listas como valores para dados agrupados
  3. Acessar e modificar elementos em estruturas aninhadas
  4. Iterar sobre estruturas compostas com laços for
  5. Modelar um problema real usando a combinação adequada de estruturas

Conteúdo Teórico

1. Por que Combinar Estruturas?

As estruturas que você aprendeu nas aulas anteriores — listas, dicionários, tuplas e conjuntos — são mais poderosas quando usadas juntas. Dados reais raramente são simples: um cadastro de alunos, um cardápio de restaurante, um histórico de pedidos — todos exigem estruturas compostas.

# Apenas uma lista de nomes — limitado
alunos = ["Ana", "Bruno", "Carlos"]

# Lista de dicionários — representa alunos com atributos
alunos = [
{"nome": "Ana", "matricula": "001", "media": 8.5},
{"nome": "Bruno", "matricula": "002", "media": 7.0},
{"nome": "Carlos", "matricula": "003", "media": 9.2},
]

A segunda versão modela fielmente a realidade: cada aluno tem múltiplos atributos, e a turma é uma coleção de alunos.


2. Lista de Dicionários

O padrão lista de dicionários é o mais comum em Python. É usado para representar tabelas de dados — cada dicionário é uma linha (registro), e as chaves são as colunas (atributos).

cardapio = [
{"nome": "X-Burguer", "preco": 18.90, "vegetariano": False},
{"nome": "X-Veggie", "preco": 17.50, "vegetariano": True},
{"nome": "Hot Dog Simples", "preco": 12.00, "vegetariano": False},
{"nome": "Salada Caesar", "preco": 22.00, "vegetariano": True},
]

Acessando um registro

primeiro = cardapio[0] # dicionário do primeiro item
print(primeiro["nome"]) # X-Burguer
print(cardapio[1]["preco"]) # 17.50 — acesso direto em uma linha

Iterando sobre todos os registros

for item in cardapio:
print(f"{item['nome']}: R$ {item['preco']:.2f}")
# X-Burguer: R$ 18.90
# X-Veggie: R$ 17.50
# ...

Filtrando registros com condição

vegetarianos = [item for item in cardapio if item["vegetariano"]]
for item in vegetarianos:
print(item["nome"])
# X-Veggie
# Salada Caesar
List comprehension

[expressao for elemento in lista if condicao] é uma forma compacta de criar uma nova lista filtrando outra. É opcional neste curso, mas vale conhecer — você verá muito em código Python real.

Calculando sobre a coleção

# Preço médio do cardápio
total = sum(item["preco"] for item in cardapio)
media = total / len(cardapio)
print(f"Preço médio: R$ {media:.2f}")

# Item mais caro
mais_caro = max(cardapio, key=lambda item: item["preco"])
print(f"Mais caro: {mais_caro['nome']}")

3. Dicionário com Listas como Valores

Outro padrão frequente é o dicionário de listas — cada chave representa uma categoria, e o valor é uma lista de itens dessa categoria.

# Notas de alunos por disciplina
notas_por_disciplina = {
"Algoritmos": [8.5, 7.0, 9.2, 6.5],
"Matemática": [7.5, 8.0, 6.0, 9.5],
"Inglês": [9.0, 8.5, 7.5, 8.0],
}

Acessando e modificando

print(notas_por_disciplina["Algoritmos"]) # [8.5, 7.0, 9.2, 6.5]
print(notas_por_disciplina["Matemática"][0]) # 7.5 — primeira nota de Matemática

# Adicionar nova nota
notas_por_disciplina["Algoritmos"].append(7.8)

# Adicionar nova disciplina
notas_por_disciplina["Física"] = [6.5, 7.0]

Iterando e calculando

for disciplina, notas in notas_por_disciplina.items():
media = sum(notas) / len(notas)
print(f"{disciplina}: média = {media:.1f}")
# Algoritmos: média = 7.8
# Matemática: média = 7.8
# Inglês: média = 8.2
# Física: média = 6.8

4. Dicionário de Dicionários

Para registros com atributos mais ricos, um dicionário de dicionários é natural. A chave externa identifica o item; o valor interno armazena seus atributos.

estoque = {
"notebook": {"quantidade": 10, "preco": 3500.00, "categoria": "eletronicos"},
"caderno": {"quantidade": 150, "preco": 12.90, "categoria": "papelaria"},
"caneta": {"quantidade": 500, "preco": 2.50, "categoria": "papelaria"},
}

print(estoque["notebook"]["preco"]) # 3500.0

# Listar itens com estoque baixo (menos de 20 unidades)
for produto, dados in estoque.items():
if dados["quantidade"] < 20:
print(f"Estoque baixo: {produto} ({dados['quantidade']} unidades)")
# Estoque baixo: notebook (10 unidades)

5. Estruturas com Tuplas

Tuplas aparecem em estruturas compostas principalmente como chaves de dicionário (listas não podem ser chaves) ou como registros imutáveis dentro de listas.

# Histórico de transações (imutável por natureza)
transacoes = [
("2026-05-01", "depósito", 1500.00),
("2026-05-03", "saque", 200.00),
("2026-05-10", "transferência", 800.00),
]

for data, tipo, valor in transacoes:
print(f"{data} | {tipo.capitalize()}: R$ {valor:.2f}")

6. Exemplo Completo: Sistema de Turma

Vamos modelar uma turma com operações comuns:

turma = [
{"nome": "Ana", "notas": [8.0, 9.5, 7.0], "aprovado": None},
{"nome": "Bruno", "notas": [4.5, 5.0, 6.0], "aprovado": None},
{"nome": "Carol", "notas": [9.0, 8.5, 10.0], "aprovado": None},
{"nome": "Diego", "notas": [3.0, 4.0, 5.5], "aprovado": None},
]

# Calcular média e definir aprovação (média >= 6.0 aprova)
for aluno in turma:
media = sum(aluno["notas"]) / len(aluno["notas"])
aluno["media"] = round(media, 1)
aluno["aprovado"] = media >= 6.0

# Exibir relatório
print(f"{'Nome':<10} {'Média':>6} {'Situação'}")
print("-" * 30)
for aluno in turma:
situacao = "Aprovado" if aluno["aprovado"] else "Reprovado"
print(f"{aluno['nome']:<10} {aluno['media']:>6.1f} {situacao}")

# Estatísticas gerais
aprovados = [a for a in turma if a["aprovado"]]
print(f"\nAprovados: {len(aprovados)}/{len(turma)}")
medias = [a["media"] for a in turma]
print(f"Média da turma: {sum(medias)/len(medias):.1f}")

Saída:

Nome Média Situação
------------------------------
Ana 8.2 Aprovado
Bruno 5.2 Reprovado
Carol 9.2 Aprovado
Diego 4.2 Reprovado

Aprovados: 2/4
Média da turma: 6.7

7. Boas Práticas ao Combinar Estruturas

  1. Consistência de chaves: em uma lista de dicionários, todos os dicionários devem ter as mesmas chaves. Chaves ausentes causam KeyError inesperado — use .get() com valor padrão quando necessário.

  2. Legibilidade: prefira estruturas que se leem naturalmente. aluno["nome"] é mais claro que aluno[0].

  3. Não aninhe além do necessário: três níveis de aninhamento (a[b][c][d]) já dificultam a leitura. Se o dado se tornar muito complexo, pode ser hora de pensar em funções ou classes.

  4. Documente a estrutura esperada: um comentário descrevendo o formato do dado ajuda muito quem lê o código depois.


Exercícios / Checkpoints

Checkpoint 1: Lista de dicionários — criação e acesso

Crie uma lista com pelo menos 4 filmes, onde cada filme é um dicionário com as chaves titulo, ano, genero e nota (de 0 a 10). Em seguida:

  1. Imprima o título e nota de cada filme
  2. Imprima apenas os filmes com nota acima de 7.5
  3. Imprima o título do filme com a maior nota

Checkpoint 2: Dicionário com listas — agrupamento

Dado o histórico de compras abaixo, organize os produtos por categoria usando um dicionário onde cada chave é uma categoria e o valor é uma lista de nomes de produtos:

compras = [
{"produto": "Notebook", "categoria": "Eletrônicos", "preco": 3500},
{"produto": "Mouse", "categoria": "Eletrônicos", "preco": 80},
{"produto": "Caderno", "categoria": "Papelaria", "preco": 15},
{"produto": "Monitor", "categoria": "Eletrônicos", "preco": 900},
{"produto": "Caneta", "categoria": "Papelaria", "preco": 3},
{"produto": "Teclado", "categoria": "Eletrônicos", "preco": 150},
]

(Resultado esperado: {"Eletrônicos": ["Notebook", "Mouse", "Monitor", "Teclado"], "Papelaria": ["Caderno", "Caneta"]})

Checkpoint 3: Estrutura completa com operações

Implemente o sistema de turma do exemplo acima (seção 6) com os seguintes complementos:

  1. Encontre o aluno com a maior média e o com a menor
  2. Exiba as notas de cada aluno em ordem crescente (sem modificar a estrutura original)
  3. Crie uma nova lista contendo apenas os nomes dos alunos aprovados

Checkpoint 4: Modelagem

Você precisa representar o cardápio de uma lanchonete com categorias (Lanches, Bebidas, Sobremesas). Cada item tem nome, preço e se está disponível. Projete a estrutura de dados mais adequada, crie um exemplo com pelo menos 2 itens por categoria e escreva um código que imprima o menu formatado mostrando apenas itens disponíveis.


Situações Problema

As duas situações abaixo são para implementação em aula. Elas exigem a combinação de vários conceitos da aula — não há uma única solução certa. Incentive os alunos a experimentar, errar e discutir abordagens diferentes.


Situação Problema 1: Catálogo de Filmes para Plataforma de Streaming

Contexto: Uma plataforma de streaming mantém seu catálogo como lista de dicionários. Cada filme possui título, ano, gênero, nota e disponibilidade.

Código inicial:

catalogo = [
{"titulo": "O Poderoso Chefão", "ano": 1972, "genero": "Drama", "nota": 9.2, "disponivel": True},
{"titulo": "Matrix", "ano": 1999, "genero": "Ficção Científica", "nota": 8.7, "disponivel": True},
{"titulo": "Toy Story", "ano": 1995, "genero": "Animação", "nota": 8.3, "disponivel": True},
{"titulo": "Parasita", "ano": 2019, "genero": "Drama", "nota": 8.6, "disponivel": False},
{"titulo": "Homem-Aranha", "ano": 2002, "genero": "Ação", "nota": 7.3, "disponivel": True},
{"titulo": "Interestelar", "ano": 2014, "genero": "Ficção Científica", "nota": 8.6, "disponivel": True},
{"titulo": "Up: Altas Aventuras", "ano": 2009, "genero": "Animação", "nota": 8.2, "disponivel": True},
{"titulo": "Gladiador", "ano": 2000, "genero": "Ação", "nota": 8.5, "disponivel": False},
{"titulo": "Coco", "ano": 2017, "genero": "Animação", "nota": 8.4, "disponivel": True},
{"titulo": "John Wick", "ano": 2014, "genero": "Ação", "nota": 7.4, "disponivel": True},
]

Tarefas:

  1. Exiba apenas os filmes disponíveis com nota >= 7.5, em ordem decrescente de nota.
  2. Calcule a nota média por gênero (apenas filmes disponíveis), em ordem decrescente.
  3. Entre todos os filmes, encontre o de maior nota e o mais antigo.
  4. Conte quantos filmes existem por gênero (sem filtro de disponibilidade).
Dica

Para ordenar por campo numérico em ordem decrescente, use sorted(lista, key=lambda item: item["campo"], reverse=True).

Ver solução
top = []
for f in catalogo:
if f["disponivel"] and f["nota"] >= 7.5:
top.append(f)

# ordenacao manual por nota (maior para menor)
for i in range(len(top)):
indice_maior = i
for j in range(i + 1, len(top)):
if top[j]["nota"] > top[indice_maior]["nota"]:
indice_maior = j
temp = top[i]
top[i] = top[indice_maior]
top[indice_maior] = temp

notas_por_genero = {}
for f in catalogo:
if f["disponivel"]:
genero = f["genero"]
if genero not in notas_por_genero:
notas_por_genero[genero] = []
notas_por_genero[genero].append(f["nota"])

medias = {}
for genero, notas in notas_por_genero.items():
medias[genero] = sum(notas) / len(notas)

# ordenacao manual das medias por genero (maior para menor)
medias_lista = list(medias.items())
for i in range(len(medias_lista)):
indice_maior = i
for j in range(i + 1, len(medias_lista)):
if medias_lista[j][1] > medias_lista[indice_maior][1]:
indice_maior = j
temp = medias_lista[i]
medias_lista[i] = medias_lista[indice_maior]
medias_lista[indice_maior] = temp

print(medias_lista)

melhor = catalogo[0]
antigo = catalogo[0]
for f in catalogo:
if f["nota"] > melhor["nota"]:
melhor = f
if f["ano"] < antigo["ano"]:
antigo = f
print(melhor["titulo"], antigo["titulo"])

contagem = {}
for f in catalogo:
genero = f["genero"]
if genero not in contagem:
contagem[genero] = 0
contagem[genero] += 1
print(contagem)

Situação Problema 2: Boletim Escolar com Estrutura Combinada

Contexto: O sistema acadêmico recebe lançamentos de notas como uma lista plana de dicionários. Um aluno pode ter múltiplos lançamentos por disciplina.

Código inicial:

lancamentos = [
{"aluno": "Ana", "disciplina": "Algoritmos", "nota": 8.5},
{"aluno": "Ana", "disciplina": "Matematica", "nota": 7.0},
{"aluno": "Ana", "disciplina": "Algoritmos", "nota": 9.0},
{"aluno": "Bruno", "disciplina": "Algoritmos", "nota": 4.0},
{"aluno": "Bruno", "disciplina": "Matematica", "nota": 6.0},
{"aluno": "Bruno", "disciplina": "Algoritmos", "nota": 5.5},
{"aluno": "Carlos", "disciplina": "Algoritmos", "nota": 9.5},
{"aluno": "Carlos", "disciplina": "Matematica", "nota": 8.0},
{"aluno": "Carlos", "disciplina": "Algoritmos", "nota": 8.5},
{"aluno": "Ana", "disciplina": "Matematica", "nota": 8.0},
{"aluno": "Bruno", "disciplina": "Matematica", "nota": 5.0},
{"aluno": "Carlos", "disciplina": "Matematica", "nota": 9.0},
{"aluno": "Diana", "disciplina": "Algoritmos", "nota": 6.0},
{"aluno": "Diana", "disciplina": "Matematica", "nota": 4.5},
{"aluno": "Diana", "disciplina": "Algoritmos", "nota": 7.0},
{"aluno": "Diana", "disciplina": "Matematica", "nota": 3.5},
]

Tarefas:

  1. Construa a estrutura boletim[aluno][disciplina] = [nota1, nota2, ...].
  2. Calcule nota final por disciplina e média geral de cada aluno.
  3. Liste alunos reprovados em pelo menos uma disciplina (nota final < 5.0).
  4. Exiba um relatório formatado com as informações consolidadas.
Dica

Na construção do boletim, inicialize em dois níveis: if aluno not in boletim: boletim[aluno] = {} e if disciplina not in boletim[aluno]: boletim[aluno][disciplina] = [].

Ver solução
boletim = {}
for r in lancamentos:
aluno = r["aluno"]
disc = r["disciplina"]
nota = r["nota"]
if aluno not in boletim:
boletim[aluno] = {}
if disc not in boletim[aluno]:
boletim[aluno][disc] = []
boletim[aluno][disc].append(nota)

resultado = {}
for aluno, disciplinas in boletim.items():
finais = {}
for disc, notas in disciplinas.items():
finais[disc] = sum(notas) / len(notas)
media_geral = sum(finais.values()) / len(finais)
resultado[aluno] = {"disciplinas": finais, "media_geral": media_geral}

reprovacoes = {}
for aluno, dados in resultado.items():
ruins = []
for disc, nf in dados["disciplinas"].items():
if nf < 5.0:
ruins.append(disc)
if ruins:
reprovacoes[aluno] = ruins

print(resultado)
print("Reprovações:", reprovacoes)

Resumo da Aula

Nesta aula, você aprendeu:

  • Lista de dicionários: padrão para coleções de registros (tabelas de dados)
  • Dicionário de listas: padrão para dados agrupados por categoria
  • Dicionário de dicionários: padrão para registros com atributos ricos acessados por identificador
  • Acesso a elementos aninhados com múltiplos colchetes: estrutura[chave][indice]
  • Iteração sobre estruturas compostas com for e .items()

Próxima aula: Entrada/Saída e Tratamento de Exceções — leitura de arquivos, entrada do usuário e tratamento de erros


Referências

Leitura Essencial

  • Python Software Foundation. Data Structures. Disponível em: docs.python.org/3/tutorial/datastructures.html
    • Tutorial oficial sobre estruturas de dados do Python, incluindo composição de estruturas.
  • Forbellone, A. L. V.; Eberspächer, H. F. Lógica de Programação: A Construção de Algoritmos e Estruturas de Dados. 3ª ed. São Paulo: Pearson, 2005.
    • Capítulo sobre estruturas compostas e registros fundamenta a modelagem de dados.

Aprofundamento (Opcional)

  • Lutz, M. Aprendendo Python. 5ª ed. São Paulo: O'Reilly Brasil, 2013.
    • Aprofundamento em listas por compreensão e manipulação avançada de estruturas compostas.
  • Python Software Foundation. sorted() Built-in Function. Disponível em: docs.python.org/3/library/functions.html#sorted
    • Como ordenar listas de dicionários por um campo específico usando o parâmetro key.