Dicionários e Conjuntos

Nas últimas duas lições cobrimos listas e tuplas, que são acessadas "por posição." Mas na vida real, frequentemente buscamos coisas "por nome" — dicionários pela palavra, contatos pelo nome. Dicionários (dict) lidam com isso. Conjuntos (set) são para operações matemáticas de conjuntos. Ambos são extremamente úteis no processamento de dados.


1. O que é um Dicionário

Um dicionário é definido com chaves {}, contendo um conjunto de pares chave-valor, separados por dois pontos.

PYTHON
# Define um dicionário
student = {
    "name": "Alice",
    "age": 20,
    "city": "São Paulo"
}

print(student)               # {'name': 'Alice', 'age': 20, 'city': 'São Paulo'}
print(type(student))         # <class 'dict'>
💡 Pense em um dicionário como um dicionário real: "chaves" são os verbetes, "valores" são as definições. Consulte uma chave e instantaneamente obtenha seu valor. A busca em dicionários é extremamente rápida — quer haja 10 entradas ou 100.000, a velocidade é quase a mesma.

Operações CRUD Básicas

PYTHON
# Cria um dicionário
user = {}

# Adiciona / modifica pares chave-valor
user["name"] = "Alice"
user["age"] = 25
print(user)                 # {'name': 'Alice', 'age': 25}

# Modifica uma chave existente
user["age"] = 26
print(user)                 # {'name': 'Alice', 'age': 26}

# Lê um valor
print(user["name"])         # Alice
# print(user["email"])      # KeyError! Chave inexistente levanta erro

# Use get() para acesso seguro — retorna padrão se chave não existe
print(user.get("email"))             # None — não encontrado, retorna None
print(user.get("email", "não definido"))  # não definido — padrão personalizado

# Remove um par chave-valor
del user["age"]
print(user)                 # {'name': 'Alice'}
⚠️ Observação: Usar dict["chave_inexistente"] levanta KeyError. Use get() para evitar isso — retorna None ou seu padrão especificado.

Exemplo: Gerenciamento de Informações de Aluno (Dificuldade ⭐)

PYTHON
# Armazena informações completas de um aluno em um dicionário
student = {
    "name": "Bob",
    "id": "2024001",
    "scores": {"Português": 85, "Matemática": 92, "Inglês": 78},
    "is_active": True
}

# Lê informações básicas
print(f"Nome: {student['name']}")
print(f"ID: {student['id']}")

# Lê de dicionário aninhado
math_score = student["scores"]["Matemática"]
print(f"Nota de Matemática: {math_score}")

# Acesso seguro com get
phone = student.get("phone", "não preenchido")
print(f"Telefone: {phone}")
▶ Experimente

Saída:

TEXT
Nome: Bob
ID: 2024001
Nota de Matemática: 92
Telefone: não preenchido

2. Métodos Comuns de Dicionário

Obtendo Todas as Chaves, Valores e Pares Chave-Valor

PYTHON
user = {"name": "Alice", "age": 25, "city": "São Paulo"}

print(user.keys())      # dict_keys(['name', 'age', 'city'])
print(user.values())    # dict_values(['Alice', 25, 'São Paulo'])
print(user.items())     # dict_items([('name', 'Alice'), ('age', 25), ('city', 'São Paulo')])

# Forma mais comum de iterar um dicionário
for key, value in user.items():
    print(f"{key} = {value}")

Saída:

TEXT
name = Alice
age = 25
city = São Paulo

pop() e setdefault()

PYTHON
# pop() — remove e retorna o valor de uma chave
user = {"name": "Alice", "age": 25, "city": "São Paulo"}
city = user.pop("city")
print(f"Cidade removida: {city}")      # Cidade removida: São Paulo
print(user)                          # {'name': 'Alice', 'age': 25}

# pop() pode especificar um padrão, evitando KeyError
email = user.pop("email", "sem email")
print(email)                         # sem email — chave não existe, retorna padrão

# setdefault() — se chave existe, retorna seu valor; se não, insere o padrão
user = {"name": "Alice"}
result = user.setdefault("age", 18)
print(user)                          # {'name': 'Alice', 'age': 18}

# Se chave já existe, setdefault não a modifica
result = user.setdefault("name", "Bob")
print(user)                          # {'name': 'Alice', 'age': 18} — inalterado

update() — Mesclando Dicionários

PYTHON
# Mescla dois dicionários
defaults = {"theme": "light", "lang": "pt", "font_size": 14}
custom = {"theme": "dark", "font_size": 16}

defaults.update(custom)    # custom sobrescreve chaves correspondentes em defaults
print(defaults)            # {'theme': 'dark', 'lang': 'pt', 'font_size': 16}

Exemplo: Contagem de Palavras (Dificuldade ⭐⭐)

PYTHON
# Conta ocorrências de palavras em uma frase
text = "maçã banana maçã laranja banana maçã"

word_count = {}
for word in text.split():
    # Se palavra já está no dict, incrementa; senão, define como 1
    word_count[word] = word_count.get(word, 0) + 1

print(word_count)          # {'maçã': 3, 'banana': 2, 'laranja': 1}

# Saída com items()
for word, count in word_count.items():
    print(f"{word}: {count} vezes")
▶ Experimente

Saída:

TEXT
maçã: 3 vezes
banana: 2 vezes
laranja: 1 vezes

3. Iteração e Aninhamento de Dicionários

Três Formas de Iterar um Dicionário

PYTHON
user = {"name": "Alice", "age": 25, "city": "São Paulo"}

# Método 1: iterar sobre chaves
for key in user:                # Equivalente a for key in user.keys():
    print(f"{key}: {user[key]}")

# Método 2: iterar sobre valores
for value in user.values():
    print(value)

# Método 3: iterar sobre pares chave-valor (recomendado)
for key, value in user.items():
    print(f"{key} → {value}")

Dicionários Aninhados

PYTHON
# Dicionário aninhado — armazenando múltiplos registros de alunos
students = {
    "1001": {"name": "Alice", "scores": [85, 90, 78]},
    "1002": {"name": "Bob", "scores": [92, 88, 95]},
    "1003": {"name": "Charlie", "scores": [76, 85, 82]},
}

# Iterar sobre dicionário aninhado
for sid, info in students.items():
    name = info["name"]
    avg = sum(info["scores"]) / len(info["scores"])
    print(f"{sid} {name}: Média {avg:.1f}")

Saída:

TEXT
1001 Alice: Média 84.3
1002 Bob: Média 91.7
1003 Charlie: Média 81.0

4. O que é um Conjunto (Set)

Um conjunto é definido com chaves {} ou set(). Parece um dicionário mas não tem valores, apenas chaves. Conjuntos são: elementos únicos, não ordenados.

PYTHON
# Define um conjunto
fruits = {"maçã", "banana", "laranja", "maçã"}    # Duplicatas são removidas automaticamente
print(fruits)            # {'laranja', 'maçã', 'banana'} — ordem pode variar

# Cria um conjunto a partir de uma lista
numbers = set([1, 2, 2, 3, 3, 3])
print(numbers)           # {1, 2, 3} — duplicatas removidas

# Conjunto vazio deve usar set(), não {} ({} é um dict vazio)
empty_set = set()
print(type(empty_set))   # <class 'set'>
💡 O uso mais comum de conjuntos é a deduplicação: list(set(lista)) remove duplicatas rapidamente. Mas observe que conjuntos não são ordenados, então a ordem original é perdida.

Operações Básicas de Conjunto

PYTHON
# Adicionar e remover
s = set()
s.add("maçã")
s.add("banana")
s.add("maçã")           # Adicionar duplicata não tem efeito
print(s)                 # {'maçã', 'banana'}

s.discard("maçã")       # Remove; não dá erro se não encontrado
print(s)                 # {'banana'}

# s.remove("inexistente")   # Levantaria KeyError!

5. Operações com Conjuntos

É aqui que os conjuntos realmente brilham — interseção, união e diferença matemáticas:

PYTHON
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

# Interseção — elementos presentes em ambos os conjuntos
print(a & b)             # {4, 5}
print(a.intersection(b)) # Mesmo

# União — todos os elementos de ambos (sem duplicatas)
print(a | b)             # {1, 2, 3, 4, 5, 6, 7, 8}
print(a.union(b))        # Mesmo

# Diferença — elementos em a mas não em b
print(a - b)             # {1, 2, 3}
print(a.difference(b))   # Mesmo

# Diferença simétrica — elementos em a ou b mas não em ambos
print(a ^ b)             # {1, 2, 3, 6, 7, 8}

Exemplo: Análise de Permissões de Usuário (Dificuldade ⭐⭐⭐)

PYTHON
# Simula relacionamentos de usuários em uma plataforma social
all_users = {"Alice", "Bob", "Charlie", "Diana", "Eve"}
vip_users = {"Bob", "Diana", "Eve"}
blocked_users = {"Diana"}

# Encontra "usuários normais que não são VIP"
normal_users = all_users - vip_users - blocked_users
print(f"Usuários normais: {normal_users}")

# Encontra "VIPs ativos (não bloqueados)"
active_vip = vip_users - blocked_users
print(f"VIPs ativos: {active_vip}")

# Encontra "usuários bloqueados que são VIP"
blocked_vip = vip_users & blocked_users
print(f"VIPs bloqueados: {blocked_vip}")

# Encontra "todos os usuários que precisam de notificação (VIPs + bloqueados)"
notify_users = vip_users | blocked_users
print(f"Usuários para notificar: {notify_users}")
▶ Experimente

Saída:

TEXT
Usuários normais: {'Alice', 'Charlie'}
VIPs ativos: {'Bob', 'Eve'}
VIPs bloqueados: {'Diana'}
Usuários para notificar: {'Bob', 'Diana', 'Eve'}

Casos de Uso Comuns


❓ Perguntas Frequentes

P: Quando devo usar um dicionário vs uma lista? R: Se você está buscando dados por "nome" (chave), use um dicionário — ex.: buscar um aluno por ID. Se você só precisa acessar dados "sequencialmente," use uma lista — ex.: um carrinho de compras. A busca em dicionários é muito mais rápida que em listas — a diferença é significativa com grandes conjuntos de dados (milhares de itens). ⚠️ P: Quais restrições as chaves de dicionário têm? Por que listas não podem ser chaves? R: Chaves de dicionário devem ser imutáveis — números, strings e tuplas funcionam. Listas são mutáveis, então não podem ser chaves. Se uma lista fosse chave e seu conteúdo mudasse, o dicionário não seria capaz de encontrá-la. Esta é a mesma razão pela qual tuplas podem ser chaves — são imutáveis, então são seguras. P: Qual a diferença entre discard() e remove()? R: remove() levanta KeyError se o elemento não existir. discard() não — remove se presente, ou silenciosamente não faz nada se não estiver. Se você não tem certeza se um elemento existe, use discard() por segurança.


📖 Resumo


📝 Atividades

  1. Básico (Dificuldade ⭐): Crie um dicionário com suas informações pessoais (nome, idade, cidade, hobby). Use get() para ler um campo "profissão" (retorne "não preenchido" se faltar). Use items() para exibir todos os pares chave-valor.

  2. Intermediário (Dificuldade ⭐⭐): Dada a frase "hello world hello python world hello", conte as ocorrências de cada palavra e encontre a palavra mais frequente. Dica: Use um dicionário para contagem; use max(dict.items(), key=lambda x: x[1]) para encontrar o máximo.

  3. Desafio (Dificuldade ⭐⭐⭐): Escreva um programa "Agenda de Contatos Simples." Use um dicionário (nome → telefone). Suporte: add Alice 13800138000, del Alice, find Alice, all (listar todos). Use while True; exit para sair. Dica: Use split() para analisar comandos.

100%