Python & AI Tutorials Logo
Programação Python

16. Dicionários: Mapeando Chaves para Valores

Nos capítulos anteriores, aprendemos sobre listas e tuplas — coleções que armazenam itens em uma ordem específica e nos deixam acessá-los por posição. Mas e se quisermos buscar informações por algo mais significativo do que um número? E se quisermos encontrar a nota de um aluno pelo nome, ou o preço de um produto pelo ID, ou a definição de uma palavra pela própria palavra?

É aqui que entram os dicionários. Um dicionário é a estrutura de dados embutida do Python para armazenar pares chave-valor (key-value pairs). Em vez de acessar itens pela posição (como grades[0]), nós os acessamos pela chave (como grades["Alice"]). Isso torna os dicionários incrivelmente poderosos para organizar e recuperar dados em programas do mundo real.

Pense em um dicionário como um dicionário do mundo real ou uma lista telefônica: você procura uma palavra (a chave) para encontrar sua definição (o valor), ou procura um nome para encontrar um número de telefone. Os dicionários do Python funcionam da mesma forma — eles mapeiam chaves para valores, permitindo buscas rápidas e uma organização flexível dos dados.

16.1) Criando Dicionários e Acessando Valores

16.1.1) O Que É um Dicionário?

Um dicionário é uma coleção de pares chave-valor. Cada chave está associada a um valor, e você usa a chave para recuperar o valor. As chaves precisam ser únicas dentro de um dicionário — você não pode ter duas entradas com a mesma chave. Já os valores podem se repetir.

Aqui está a estrutura básica:

  • Chaves: identificadores únicos usados para buscar valores (como nomes, IDs ou rótulos)
  • Valores: os dados associados a cada chave (como notas, preços ou descrições)

Dicionários são:

  • Mutáveis: você pode adicionar, modificar ou remover pares chave-valor após a criação
  • Não ordenados (no Python 3.6 e anteriores) ou ordenados por inserção (no Python 3.7+): embora o Python moderno preserve a ordem em que você adiciona itens, você deve pensar em dicionários como coleções em que você acessa itens por chave, não por posição
  • Dinâmicos: eles podem crescer e encolher conforme necessário

16.1.2) Criando Dicionários Vazios e Simples

A forma mais simples de criar um dicionário é usando chaves {} com pares chave-valor separados por dois-pontos:

python
# Dicionário vazio
empty_dict = {}
print(empty_dict)  # Output: {}
print(type(empty_dict))  # Output: <class 'dict'>
 
# Dicionário com notas de alunos
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
 
# Dicionário com preços de produtos
prices = {"apple": 0.50, "banana": 0.30, "orange": 0.75}
print(prices)  # Output: {'apple': 0.5, 'banana': 0.3, 'orange': 0.75}

Observe a sintaxe: cada par chave-valor é escrito como key: value, e os pares são separados por vírgulas. As chaves aqui são strings ("Alice", "apple"), e os valores são números, mas tanto chaves quanto valores podem ser de muitos tipos diferentes.

Você também pode criar um dicionário usando o construtor dict():

python
# Usando dict() com argumentos nomeados
student = dict(name="Alice", age=20, major="Computer Science")
print(student)  # Output: {'name': 'Alice', 'age': 20, 'major': 'Computer Science'}
 
# Usando dict() com uma lista de tuplas
colors = dict([("red", "#FF0000"), ("green", "#00FF00"), ("blue", "#0000FF")])
print(colors)  # Output: {'red': '#FF0000', 'green': '#00FF00', 'blue': '#0000FF'}

O construtor dict() é útil quando você está montando dicionários a partir de outras estruturas de dados ou quando quer usar identificadores do Python como chaves (sem aspas).

16.1.3) Acessando Valores pela Chave

Para recuperar um valor de um dicionário, use colchetes com a chave:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Acessar valores individuais
alice_grade = grades["Alice"]
print(alice_grade)  # Output: 95
 
bob_grade = grades["Bob"]
print(bob_grade)  # Output: 87

Esta é a forma mais direta de acessar valores de um dicionário. Porém, se você tentar acessar uma chave que não existe, o Python levanta um KeyError:

python
grades = {"Alice": 95, "Bob": 87}
 
# AVISO: KeyError - apenas para demonstração
# print(grades["David"])  # PROBLEM: KeyError: 'David'

Esse erro acontece porque "David" não é uma chave no dicionário. Vamos aprender como lidar com isso com segurança na próxima subseção.

16.1.4) Acesso Seguro com get()

Para evitar KeyError quando uma chave pode não existir, use o método get(). Ele retorna None (ou um valor padrão que você especificar) se a chave não for encontrada:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Acesso seguro com get()
alice_grade = grades.get("Alice")
print(alice_grade)  # Output: 95
 
# Chave não existe - retorna None
david_grade = grades.get("David")
print(david_grade)  # Output: None
 
# Fornecer um valor padrão
david_grade = grades.get("David", 0)
print(david_grade)  # Output: 0
 
# Usando get() em lógica condicional
if grades.get("Eve") is None:
    print("Eve is not in the grade book")  # Output: Eve is not in the grade book

O método get() é mais seguro do que o acesso direto por colchetes quando você não tem certeza de que uma chave existe. O segundo argumento de get() é o valor padrão a ser retornado se a chave estiver ausente — se você não fornecer um, o padrão é None.

Aqui vai um exemplo prático mostrando quando get() é útil:

python
# Banco de dados de alunos com informações opcionais
students = {
    "Alice": {"age": 20, "major": "CS"},
    "Bob": {"age": 19},  # Bob ainda não declarou uma área
    "Charlie": {"major": "Math"}  # A idade de Charlie não foi registrada
}
 
# Acessar com segurança informações que podem estar ausentes
for name in ["Alice", "Bob", "Charlie"]:
    student = students[name]
    age = student.get("age", "Unknown")
    major = student.get("major", "Undeclared")
    print(f"{name}: Age {age}, Major {major}")
 
# Output:
# Alice: Age 20, Major CS
# Bob: Age 19, Major Undeclared
# Charlie: Age Unknown, Major Math

16.1.5) Tipos de Chave Válidos

As chaves de um dicionário precisam ser hashable — um termo técnico que significa que o valor da chave não pode mudar. Na prática, isso significa:

Tipos de chave válidos (imutáveis):

  • Strings: "name", "id_123"
  • Números: 42, 3.14
  • Tuplas (se contiverem apenas itens imutáveis): (1, 2), ("x", "y")
  • Booleanos: True, False
  • None

Tipos de chave inválidos (mutáveis):

  • Listas: [1, 2, 3] não podem ser chave
  • Dicionários: {"a": 1} não podem ser chave
  • Conjuntos: {1, 2, 3} não podem ser chave
python
# Chaves válidas
valid_dict = {
    "name": "Alice",           # Chave string
    42: "answer",              # Chave inteira
    3.14: "pi",                # Chave float
    (1, 2): "coordinates",     # Chave tupla
    True: "yes",               # Chave booleana
    None: "nothing"            # Chave None
}
print(valid_dict["name"])      # Output: Alice
print(valid_dict[42])          # Output: answer
print(valid_dict[(1, 2)])      # Output: coordinates
 
# AVISO: Chaves inválidas - apenas para demonstração
# invalid_dict = {[1, 2]: "list key"}  # PROBLEM: TypeError: unhashable type: 'list'
# invalid_dict = {{1, 2}: "set key"}   # PROBLEM: TypeError: unhashable type: 'set'

Erro comum de iniciante: um erro frequente é tentar usar uma lista como chave de um dicionário porque isso parece lógico. Por exemplo, você pode querer usar coordenadas [x, y] como chave para armazenar dados de localização. Quando o Python levanta TypeError: unhashable type: 'list', iniciantes muitas vezes não entendem o porquê — afinal, a lista contém exatamente os dados que eles querem usar como chave.

O motivo é que listas são mutáveis (elas podem mudar), e o Python precisa que as chaves do dicionário sejam estáveis e não mudem. Se você precisa usar algo “tipo lista” como chave, converta para tupla primeiro: tuple([1, 2]) vira (1, 2), que pode ser usado como chave. Tuplas são imutáveis, então funcionam perfeitamente:

python
# Errado: tentar usar uma lista como chave
# locations = {[0, 0]: "origin", [1, 0]: "east"}  # PROBLEM: TypeError
 
# Certo: converter para tupla
locations = {(0, 0): "origin", (1, 0): "east", (0, 1): "north"}
print(locations[(0, 0)])  # Output: origin
print(locations[(1, 0)])  # Output: east

Os valores, por outro lado, podem ser de qualquer tipo — mutáveis ou imutáveis:

python
# Valores podem ser de qualquer tipo
flexible_dict = {
    "numbers": [1, 2, 3],              # Valor lista
    "nested": {"a": 1, "b": 2},        # Valor dicionário
    "function": len,                    # Valor função
    "mixed": (1, [2, 3], {"x": 4})     # Tupla contendo itens mutáveis
}
print(flexible_dict["numbers"])        # Output: [1, 2, 3]
print(flexible_dict["nested"]["a"])    # Output: 1

Vamos explorar hashability com mais profundidade no Capítulo 17, mas por enquanto, lembre-se: use tipos imutáveis (strings, números, tuplas) como chaves, e você ficará bem.

16.2) Adicionando e Atualizando Entradas do Dicionário

16.2.1) Adicionando Novos Pares Chave-Valor

Adicionar uma nova entrada a um dicionário é simples — basta atribuir um valor a uma nova chave:

python
grades = {"Alice": 95, "Bob": 87}
print(grades)  # Output: {'Alice': 95, 'Bob': 87}
 
# Adicionar um novo aluno
grades["Charlie"] = 92
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
 
# Adicionar outro aluno
grades["Diana"] = 88
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92, 'Diana': 88}

Se a chave não existir, o Python a cria. Se ela existir, o Python atualiza o valor (vamos ver isso em seguida).

16.2.2) Atualizando Valores Existentes

Para atualizar um valor, basta atribuir um novo valor a uma chave existente:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
 
# Atualizar a nota do Bob
grades["Bob"] = 90
print(grades)  # Output: {'Alice': 95, 'Bob': 90, 'Charlie': 92}
 
# Atualizar múltiplas notas
grades["Alice"] = 98
grades["Charlie"] = 94
print(grades)  # Output: {'Alice': 98, 'Bob': 90, 'Charlie': 94}

O Python não diferencia adicionar e atualizar — a sintaxe é idêntica. Se a chave existir, o valor é atualizado; se não, uma nova entrada é criada.

Aqui vai um exemplo prático mostrando tanto adicionar quanto atualizar:

python
# Controlar estoque
inventory = {"apple": 50, "banana": 30}
print("Initial inventory:", inventory)  # Output: Initial inventory: {'apple': 50, 'banana': 30}
 
# Repor maçãs (atualizar existente)
inventory["apple"] = inventory["apple"] + 20
print("After restocking apples:", inventory)  # Output: After restocking apples: {'apple': 70, 'banana': 30}
 
# Adicionar novo produto (adicionar nova chave)
inventory["orange"] = 40
print("After adding oranges:", inventory)  # Output: After adding oranges: {'apple': 70, 'banana': 30, 'orange': 40}
 
# Vender algumas bananas (atualizar existente)
inventory["banana"] = inventory["banana"] - 10
print("After selling bananas:", inventory)  # Output: After selling bananas: {'apple': 70, 'banana': 20, 'orange': 40}

16.2.3) Usando update() para Mesclar Dicionários

O método update() adiciona múltiplos pares chave-valor de uma vez ou mescla outro dicionário no dicionário atual:

python
grades = {"Alice": 95, "Bob": 87}
print(grades)  # Output: {'Alice': 95, 'Bob': 87}
 
# Adicionar múltiplos alunos de uma vez
new_students = {"Charlie": 92, "Diana": 88}
grades.update(new_students)
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92, 'Diana': 88}
 
# Atualizar existentes e adicionar novos
more_updates = {"Bob": 90, "Eve": 85}  # A nota do Bob muda, Eve é nova
grades.update(more_updates)
print(grades)  # Output: {'Alice': 95, 'Bob': 90, 'Charlie': 92, 'Diana': 88, 'Eve': 85}

O método update() modifica o dicionário “in place”. Se uma chave já existir, seu valor é atualizado; se não, o par chave-valor é adicionado.

Você também pode passar argumentos nomeados para update():

python
student = {"name": "Alice", "age": 20}
print(student)  # Output: {'name': 'Alice', 'age': 20}
 
# Atualizar usando argumentos nomeados
student.update(age=21, major="Computer Science")
print(student)  # Output: {'name': 'Alice', 'age': 21, 'major': 'Computer Science'}

Aqui vai um exemplo prático mesclando configurações:

python
# Configurações padrão
config = {
    "theme": "light",
    "font_size": 12,
    "auto_save": True
}
 
# Preferências do usuário (sobrescrevem alguns padrões)
user_prefs = {
    "theme": "dark",
    "font_size": 14
}
 
# Mesclar preferências do usuário em config
config.update(user_prefs)
print(config)  # Output: {'theme': 'dark', 'font_size': 14, 'auto_save': True}

16.2.4) Usando setdefault() para Adicionar Apenas Se a Chave Não Existir

O método setdefault() é útil quando você quer adicionar um par chave-valor apenas se a chave ainda não existir. Se a chave existir, ele retorna o valor atual sem alterá-lo:

python
grades = {"Alice": 95, "Bob": 87}
 
# Adicionar Charlie (a chave não existe)
result = grades.setdefault("Charlie", 90)
print(result)  # Output: 90
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 90}
 
# Tentar adicionar Alice (a chave existe - sem mudança)
result = grades.setdefault("Alice", 80)
print(result)  # Output: 95 (existing value returned)
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 90} (unchanged)

Isso é particularmente útil quando você quer garantir que todas as chaves de configuração obrigatórias existam com valores padrão, preservando quaisquer valores que o usuário já tenha personalizado:

python
# Configuração do aplicativo com padrões
config = {"theme": "light", "font_size": 12}
 
# Garantir que todas as configurações obrigatórias existam com padrões
config.setdefault("auto_save", True)
config.setdefault("language", "en")
config.setdefault("theme", "dark")  # Não vai mudar - já existe
 
print(config)
# Output: {'theme': 'light', 'font_size': 12, 'auto_save': True, 'language': 'en'}
 
# Agora acessar com segurança todas as configurações
print(f"Theme: {config['theme']}")           # Output: Theme: light
print(f"Auto-save: {config['auto_save']}")   # Output: Auto-save: True
print(f"Language: {config['language']}")     # Output: Language: en

16.3) Removendo Entradas do Dicionário com del e pop()

16.3.1) Removendo Entradas com del

A instrução del remove um par chave-valor de um dicionário:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92, 'Diana': 88}
 
# Remover Charlie
del grades["Charlie"]
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Diana': 88}
 
# Remover outro aluno
del grades["Bob"]
print(grades)  # Output: {'Alice': 95, 'Diana': 88}

Se você tentar deletar uma chave que não existe, o Python levanta um KeyError:

python
grades = {"Alice": 95, "Bob": 87}
 
# AVISO: KeyError - apenas para demonstração
# del grades["Charlie"]  # PROBLEM: KeyError: 'Charlie'

Para deletar com segurança uma chave que pode não existir, verifique primeiro:

python
grades = {"Alice": 95, "Bob": 87}
 
# Exclusão segura
if "Charlie" in grades:
    del grades["Charlie"]
else:
    print("Charlie not found")  # Output: Charlie not found

16.3.2) Removendo e Recuperando com pop()

O método pop() remove uma chave e retorna seu valor. Isso é útil quando você precisa tanto remover uma entrada quanto usar o valor dela:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
 
# Remover e pegar a nota do Bob
bob_grade = grades.pop("Bob")
print(bob_grade)  # Output: 87
print(grades)  # Output: {'Alice': 95, 'Charlie': 92}
 
# Remover e usar o valor
charlie_grade = grades.pop("Charlie")
print(f"Charlie's final grade was {charlie_grade}")  # Output: Charlie's final grade was 92
print(grades)  # Output: {'Alice': 95}

Assim como del, pop() levanta um KeyError se a chave não existir. Porém, você pode fornecer um valor padrão para retornar no lugar:

python
grades = {"Alice": 95, "Bob": 87}
 
# Pop com valor padrão (a chave não existe)
diana_grade = grades.pop("Diana", 0)
print(diana_grade)  # Output: 0
print(grades)  # Output: {'Alice': 95, 'Bob': 87} (unchanged)
 
# Pop com valor padrão (a chave existe)
alice_grade = grades.pop("Alice", 0)
print(alice_grade)  # Output: 95
print(grades)  # Output: {'Bob': 87}

16.3.3) Removendo Todas as Entradas com clear()

O método clear() remove todos os pares chave-valor de um dicionário, deixando-o vazio:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
 
# Limpar todas as entradas
grades.clear()
print(grades)  # Output: {}
print(len(grades))  # Output: 0

Isso é mais explícito do que reatribuir para um dicionário vazio (grades = {}), especialmente quando outras variáveis referenciam o mesmo dicionário:

python
# Demonstrar a diferença
grades = {"Alice": 95, "Bob": 87}
backup = grades  # backup referencia o mesmo dicionário
 
# Usando clear() - afeta ambas as variáveis
grades.clear()
print(grades)   # Output: {}
print(backup)   # Output: {} (same dictionary was cleared)
 
# Resetar para o próximo exemplo
grades = {"Alice": 95, "Bob": 87}
backup = grades
 
# Reatribuir - afeta apenas grades
grades = {}
print(grades)   # Output: {}
print(backup)   # Output: {'Alice': 95, 'Bob': 87} (different dictionary now)

Vamos explorar esse comportamento mais no Capítulo 18 quando discutirmos semântica de referência, mas por enquanto, lembre-se: clear() esvazia o dicionário existente, enquanto a reatribuição cria um novo dicionário vazio.

16.4) Objetos View do Dicionário: keys(), values() e items()

16.4.1) Entendendo Views de Dicionário

Dicionários fornecem três métodos que retornam objetos view (view objects) — objetos especiais que fornecem uma visão dinâmica das chaves, valores ou pares chave-valor do dicionário. Essas views refletem mudanças no dicionário automaticamente:

  • keys(): retorna uma visão de todas as chaves
  • values(): retorna uma visão de todos os valores
  • items(): retorna uma visão de todos os pares chave-valor (como tuplas)
python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Obter visões
keys_view = grades.keys()
values_view = grades.values()
items_view = grades.items()
 
print(keys_view)    # Output: dict_keys(['Alice', 'Bob', 'Charlie'])
print(values_view)  # Output: dict_values([95, 87, 92])
print(items_view)   # Output: dict_items([('Alice', 95), ('Bob', 87), ('Charlie', 92)])

Isso não são listas — são objetos view. Mas você pode convertê-los em listas, se necessário:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Converter visões para listas
keys_list = list(grades.keys())
values_list = list(grades.values())
items_list = list(grades.items())
 
print(keys_list)    # Output: ['Alice', 'Bob', 'Charlie']
print(values_list)  # Output: [95, 87, 92]
print(items_list)   # Output: [('Alice', 95), ('Bob', 87), ('Charlie', 92)]

16.4.2) Views São Dinâmicas

Objetos view refletem mudanças no dicionário automaticamente:

python
grades = {"Alice": 95, "Bob": 87}
 
# Criar uma view
keys_view = grades.keys()
print(keys_view)  # Output: dict_keys(['Alice', 'Bob'])
 
# Modificar o dicionário
grades["Charlie"] = 92
print(keys_view)  # Output: dict_keys(['Alice', 'Bob', 'Charlie'])
 
# Remover uma entrada
del grades["Bob"]
print(keys_view)  # Output: dict_keys(['Alice', 'Charlie'])

Esse comportamento dinâmico é útil quando você precisa trabalhar com conteúdos do dicionário que podem mudar. Porém, se você precisa de uma cópia estática das chaves (um "snapshot"), converta a view em lista:

python
grades = {"Alice": 95, "Bob": 87}
 
# Criar um snapshot
keys_snapshot = list(grades.keys())
print(keys_snapshot)  # Output: ['Alice', 'Bob']
 
# Modificar o dicionário
grades["Charlie"] = 92
print(keys_snapshot)  # Output: ['Alice', 'Bob'] (unchanged)

Atualização Dinâmica

Sem Efeit

Dicionário

Objeto de Visão

Snapshot em Lista

Modificar Dicionário

16.4.3) Trabalhando com keys()

O método keys() retorna uma view de todas as chaves do dicionário. Isso é útil para verificar quais chaves existem ou iterar sobre elas:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Obter todas as chaves
keys = grades.keys()
print(keys)  # Output: dict_keys(['Alice', 'Bob', 'Charlie'])
 
# Verificar se uma chave existe
if "Alice" in keys:
    print("Alice is in the grade book")  # Output: Alice is in the grade book
 
# Contar chaves
print(f"Number of students: {len(keys)}")  # Output: Number of students: 3

Você também pode checar pertencimento diretamente no dicionário sem chamar keys():

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Estes são equivalentes
if "Alice" in grades.keys():
    print("Found (using keys())")
 
if "Alice" in grades:
    print("Found (direct check)")  # This is more common and concise
 
# Output:
# Found (using keys())
# Found (direct check)

16.4.4) Trabalhando com values()

O método values() retorna uma view de todos os valores do dicionário. Isso é útil quando você precisa processar valores sem se importar com suas chaves:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
 
# Obter todos os valores
values = grades.values()
print(values)  # Output: dict_values([95, 87, 92, 88])
 
# Calcular estatísticas
total = sum(values)
count = len(values)
average = total / count
 
print(f"Total points: {total}")      # Output: Total points: 362
print(f"Number of students: {count}")  # Output: Number of students: 4
print(f"Average grade: {average}")   # Output: Average grade: 90.5
 
# Encontrar maiores e menores notas
print(f"Highest grade: {max(values)}")  # Output: Highest grade: 95
print(f"Lowest grade: {min(values)}")   # Output: Lowest grade: 87

16.4.5) Trabalhando com items()

O método items() retorna uma view dos pares chave-valor como tuplas. Esta é a view mais usada porque te dá tanto as chaves quanto os valores:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Obter todos os pares chave-valor
items = grades.items()
print(items)  # Output: dict_items([('Alice', 95), ('Bob', 87), ('Charlie', 92)])
 
# Converter para lista para ver as tuplas com clareza
items_list = list(items)
print(items_list)  # Output: [('Alice', 95), ('Bob', 87), ('Charlie', 92)]
 
# Acessar tuplas individuais
first_item = items_list[0]
print(first_item)        # Output: ('Alice', 95)
print(first_item[0])     # Output: Alice
print(first_item[1])     # Output: 95

A view items() é particularmente útil para iteração, que vamos cobrir em detalhes na próxima seção. Aqui vai uma prévia:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Processar cada par chave-valor
for name, grade in grades.items():
    print(f"{name}: {grade}")
 
# Output:
# Alice: 95
# Bob: 87
# Charlie: 92

16.5) Iterando Sobre Chaves, Valores e Itens

16.5.1) Iterando Sobre Chaves (Comportamento Padrão)

Quando você itera diretamente sobre um dicionário com um loop for, você itera sobre suas chaves:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Iterar sobre chaves (implícito)
for name in grades:
    print(name)
 
# Output:
# Alice
# Bob
# Charlie

Isso é equivalente a iterar sobre grades.keys():

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Iterar sobre chaves (explícito)
for name in grades.keys():
    print(name)
 
# Output:
# Alice
# Bob
# Charlie

Ambas as abordagens funcionam de forma idêntica. A versão implícita (sem .keys()) é mais comum e mais concisa.

Aqui vai um exemplo prático usando chaves para acessar valores:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
 
# Encontrar alunos que passaram (nota >= 90)
passing_students = []
for name in grades:
    if grades[name] >= 90:
        passing_students.append(name)
 
print("Students who passed:", passing_students)  # Output: Students who passed: ['Alice', 'Charlie']

16.5.2) Iterando Sobre Valores

Para iterar apenas sobre os valores, use values():

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
 
# Iterar sobre valores
for grade in grades.values():
    print(grade)
 
# Output:
# 95
# 87
# 92
# 88

Isso é útil quando você precisa processar valores, mas não se importa com qual chave eles estão associados:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
 
# Calcular total e média
total = 0
count = 0
for grade in grades.values():
    total = total + grade
    count = count + 1
 
average = total / count
print(f"Class average: {average}")  # Output: Class average: 90.5
 
# Checar se todos os alunos passaram
all_passed = True
for grade in grades.values():
    if grade < 60:
        all_passed = False
        break
 
if all_passed:
    print("All students passed!")  # Output: All students passed!

16.5.3) Iterando Sobre Pares Chave-Valor com items()

O padrão de iteração mais comum e útil é usar items() para obter tanto chaves quanto valores:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Iterar sobre pares chave-valor
for name, grade in grades.items():
    print(f"{name} scored {grade}")
 
# Output:
# Alice scored 95
# Bob scored 87
# Charlie scored 92

Repare no desempacotamento de tupla: for name, grade in grades.items(). Cada item é uma tupla como ("Alice", 95), e nós a desempacotamos em duas variáveis. Isso é muito mais legível do que acessar índices de tupla:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Sem desempacotar (menos legível)
for item in grades.items():
    print(f"{item[0]} scored {item[1]}")
 
# Com desempacotamento (mais legível)
for name, grade in grades.items():
    print(f"{name} scored {grade}")
 
# Both produce the same output:
# Alice scored 95
# Bob scored 87
# Charlie scored 92

16.5.4) Modificando Dicionários Durante a Iteração

Aviso: modificar o tamanho de um dicionário (adicionando ou removendo chaves) enquanto itera sobre ele pode causar erros ou comportamentos inesperados:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# AVISO: RuntimeError - apenas para demonstração
# for name in grades:
#     if grades[name] < 90:
#         del grades[name]  # PROBLEM: RuntimeError: dictionary changed size during iteration

No Python moderno (3.7+), isso levanta imediatamente um RuntimeError assim que você tenta mudar o tamanho do dicionário. O Python detecta a modificação e para a execução para evitar comportamento imprevisível.

Em versões mais antigas do Python, isso poderia fazer o iterador:

  • Pular itens que deveria processar
  • Processar o mesmo item duas vezes
  • Produzir resultados inconsistentes

Por isso o Python agora falha rápido com uma mensagem de erro clara.

Se você precisa modificar um dicionário durante a iteração, itere sobre uma cópia das chaves:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
 
# Seguro: iterar sobre uma cópia das chaves
for name in list(grades.keys()):
    if grades[name] < 90:
        del grades[name]
 
print(grades)  # Output: {'Alice': 95, 'Charlie': 92}

Ou monte um novo dicionário com apenas as entradas que você quer:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
 
# Montar um novo dicionário com entradas filtradas
high_grades = {}
for name, grade in grades.items():
    if grade >= 90:
        high_grades[name] = grade
 
print(high_grades)  # Output: {'Alice': 95, 'Charlie': 92}

A segunda abordagem costuma ser mais clara e segura. Vamos aprender formas ainda mais elegantes de fazer isso usando dictionary comprehensions no Capítulo 35.

16.6) Métodos Comuns de Dicionário

16.6.1) Verificando Chaves com in e not in

Os operadores in e not in verificam se uma chave existe em um dicionário:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
 
# Verificar se as chaves existem
if "Alice" in grades:
    print("Alice is in the grade book")  # Output: Alice is in the grade book
 
if "David" not in grades:
    print("David is not in the grade book")  # Output: David is not in the grade book

Esta é a forma preferida de checar a existência de uma chave antes de acessar valores. É mais legível e mais “Pythonic” do que usar get() e checar None:

python
grades = {"Alice": 95, "Bob": 87}
 
# Preferido: usando in
if "Alice" in grades:
    print(f"Alice's grade: {grades['Alice']}")  # Output: Alice's grade: 95
 
# Alternativa: usando get() e checando None
if grades.get("Alice") is not None:
    print(f"Alice's grade: {grades['Alice']}")  # Output: Alice's grade: 95

16.6.2) Obtendo o Número de Entradas com len()

A função len() retorna o número de pares chave-valor em um dicionário:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(len(grades))  # Output: 3
 
# Dicionário vazio
empty = {}
print(len(empty))  # Output: 0
 
# Após modificações
grades["Diana"] = 88
print(len(grades))  # Output: 4
 
del grades["Bob"]
print(len(grades))  # Output: 3

Isso é útil para verificar se um dicionário está vazio ou para relatar estatísticas:

python
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
 
if len(grades) == 0:
    print("No students in grade book")
else:
    total = sum(grades.values())
    average = total / len(grades)
    print(f"{len(grades)} students, average grade: {average}")
    # Output: 4 students, average grade: 90.5

16.6.3) Copiando Dicionários com copy()

O método copy() cria uma cópia rasa (shallow copy) de um dicionário — um novo dicionário com os mesmos pares chave-valor:

python
original = {"Alice": 95, "Bob": 87}
duplicate = original.copy()
 
print(original)   # Output: {'Alice': 95, 'Bob': 87}
print(duplicate)  # Output: {'Alice': 95, 'Bob': 87}
 
# Modificar a cópia
duplicate["Charlie"] = 92
print(original)   # Output: {'Alice': 95, 'Bob': 87} (unchanged)
print(duplicate)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}

Isso é diferente de uma simples atribuição, que cria uma referência para o mesmo dicionário:

python
original = {"Alice": 95, "Bob": 87}
reference = original  # Não é uma cópia - mesmo dicionário
 
# Modificar através da referência
reference["Charlie"] = 92
print(original)   # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92} (changed!)
print(reference)  # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}

Vamos explorar cópias rasas vs profundas em detalhes no Capítulo 18. Por enquanto, lembre-se: use copy() quando você quiser um duplicado independente de um dicionário.

16.6.4) Mesclando Dicionários com o Operador | (Python 3.9+)

O Python 3.9 introduziu o operador | para mesclar dicionários. O operador | cria um novo dicionário combinando todas as chaves de ambos os dicionários. Para chaves duplicadas, o valor do lado direito sobrescreve o valor do lado esquerdo. Ambos os dicionários originais permanecem inalterados.

python
defaults = {"theme": "light", "font_size": 12, "auto_save": True}
user_prefs = {"theme": "dark", "font_size": 14}
 
# Mesclar dicionários (user_prefs sobrescreve defaults)
config = defaults | user_prefs
print(config)  # Output: {'theme': 'dark', 'font_size': 14, 'auto_save': True}
 
# Dicionários originais inalterados
print(defaults)    # Output: {'theme': 'light', 'font_size': 12, 'auto_save': True}
print(user_prefs)  # Output: {'theme': 'dark', 'font_size': 14}

Aqui vai outro exemplo mostrando como isso é útil para combinar dados de múltiplas fontes:

python
# Informações de produto de dois fornecedores
supplier_a = {
    "laptop": {"price": 999.99, "stock": 15},
    "mouse": {"price": 29.99, "stock": 50}
}
 
supplier_b = {
    "laptop": {"price": 949.99, "stock": 20},  # Preço e estoque melhores
    "keyboard": {"price": 79.99, "stock": 30}
}
 
# Mesclar: dados de supplier_b sobrescrevem supplier_a para produtos correspondentes
combined = supplier_a | supplier_b
print(combined)
# Output: {'laptop': {'price': 949.99, 'stock': 20}, 'mouse': {'price': 29.99, 'stock': 50}, 'keyboard': {'price': 79.99, 'stock': 30}}
 
# Agora temos laptop de supplier_b (melhor negócio), mouse de supplier_a e keyboard de supplier_b

O operador |= mescla “in place” (modifica o dicionário da esquerda):

python
config = {"theme": "light", "font_size": 12, "auto_save": True}
user_prefs = {"theme": "dark", "font_size": 14}
 
# Mesclar in place
config |= user_prefs
print(config)  # Output: {'theme': 'dark', 'font_size': 14, 'auto_save': True}

Isso é equivalente a usar update(), mas mais conciso:

python
config = {"theme": "light", "font_size": 12}
user_prefs = {"theme": "dark", "font_size": 14}
 
# Estes são equivalentes
config.update(user_prefs)
# config |= user_prefs
 
print(config)  # Output: {'theme': 'dark', 'font_size': 14}

16.7) Dicionários Aninhados para Dados Estruturados

16.7.1) Criando Dicionários Aninhados

Por que usar dicionários aninhados? Imagine acompanhar informações de alunos. Você poderia criar dicionários separados: ages = {"Alice": 20, "Bob": 19}, majors = {"Alice": "CS", "Bob": "Math"}, gpas = {"Alice": 3.8, "Bob": 3.6}. Mas isso fica difícil de gerenciar — você precisa manter três dicionários sincronizados, e buscar todas as informações de um aluno exige três buscas separadas. Dicionários aninhados resolvem isso agrupando dados relacionados: um nome de aluno mapeia para todas as informações dele em uma única busca.

Um dicionário aninhado (nested dictionary) é um dicionário que contém outros dicionários como valores. Isso é útil para representar dados estruturados e hierárquicos:

python
# Registros de alunos com múltiplos atributos
students = {
    "Alice": {
        "age": 20,
        "major": "Computer Science",
        "gpa": 3.8
    },
    "Bob": {
        "age": 19,
        "major": "Mathematics",
        "gpa": 3.6
    },
    "Charlie": {
        "age": 21,
        "major": "Physics",
        "gpa": 3.9
    }
}
 
print(students)
# Output: {'Alice': {'age': 20, 'major': 'Computer Science', 'gpa': 3.8}, 'Bob': {'age': 19, 'major': 'Mathematics', 'gpa': 3.6}, 'Charlie': {'age': 21, 'major': 'Physics', 'gpa': 3.9}}

Cada nome de aluno mapeia para outro dicionário contendo seus atributos. Essa estrutura é muito mais flexível e fácil de manter do que usar dicionários separados para cada atributo.

Dicionário students

Alice

Bob

Charlie

age: 20

major: CS

gpa: 3.8

age: 19

major: Math

gpa: 3.6

age: 21

major: Physics

gpa: 3.9

16.7.2) Acessando Valores Aninhados

Para acessar valores aninhados, use múltiplos colchetes:

python
students = {
    "Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
    "Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6}
}
 
# Acessar valores aninhados
alice_age = students["Alice"]["age"]
print(alice_age)  # Output: 20
 
bob_major = students["Bob"]["major"]
print(bob_major)  # Output: Mathematics
 
# Usar em expressões
print(f"Alice is {students['Alice']['age']} years old")
# Output: Alice is 20 years old
 
print(f"Bob's GPA: {students['Bob']['gpa']}")
# Output: Bob's GPA: 3.6

Cada colchete acessa um nível do aninhamento. Primeiro students["Alice"] pega o dicionário interno, depois ["age"] pega o valor de idade desse dicionário.

16.7.3) Acessando Valores Aninhados com Segurança

Ao acessar dicionários aninhados, você precisa verificar se cada nível existe:

python
students = {
    "Alice": {"age": 20, "major": "Computer Science"},
    "Bob": {"age": 19}  # Bob não declarou uma área
}
 
# Acesso inseguro - pode levantar KeyError
# print(students["Bob"]["major"])  # PROBLEM: KeyError: 'major'
 
# Acesso seguro com múltiplas verificações
if "Bob" in students:
    if "major" in students["Bob"]:
        print(f"Bob's major: {students['Bob']['major']}")
    else:
        print("Bob hasn't declared a major")  # Output: Bob hasn't declared a major
 
# Usando get() para acesso aninhado seguro
bob_major = students.get("Bob", {}).get("major", "Undeclared")
print(f"Bob's major: {bob_major}")  # Output: Bob's major: Undeclared

A cadeia de get() funciona porque, se "Bob" não existir, get() retorna um dicionário vazio {}, e então o segundo get() retorna com segurança "Undeclared".

16.7.4) Modificando Dicionários Aninhados

Você pode adicionar, atualizar ou remover entradas em dicionários aninhados:

python
students = {
    "Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
    "Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6}
}
 
# Atualizar um valor aninhado
students["Alice"]["gpa"] = 3.9
print(f"Alice's new GPA: {students['Alice']['gpa']}")  # Output: Alice's new GPA: 3.9
 
# Adicionar um novo atributo a um aluno existente
students["Bob"]["email"] = "bob@university.edu"
print(students["Bob"])
# Output: {'age': 19, 'major': 'Mathematics', 'gpa': 3.6, 'email': 'bob@university.edu'}
 
# Adicionar um novo aluno com dados aninhados
students["Charlie"] = {
    "age": 21,
    "major": "Physics",
    "gpa": 3.7
}
print(f"Number of students: {len(students)}")  # Output: Number of students: 3
 
# Remover um atributo
del students["Bob"]["email"]
print(students["Bob"])
# Output: {'age': 19, 'major': 'Mathematics', 'gpa': 3.6}

16.7.5) Iterando Sobre Dicionários Aninhados

Você pode iterar sobre dicionários aninhados usando loops aninhados:

python
students = {
    "Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
    "Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6},
    "Charlie": {"age": 21, "major": "Physics", "gpa": 3.9}
}
 
# Iterar sobre alunos e seus atributos
for name, info in students.items():
    print(f"\n{name}:")
    for key, value in info.items():
        print(f"  {key}: {value}")
 
# Output:
# Alice:
#   age: 20
#   major: Computer Science
#   gpa: 3.8
#
# Bob:
#   age: 19
#   major: Mathematics
#   gpa: 3.6
#
# Charlie:
#   age: 21
#   major: Physics
#   gpa: 3.9

Aqui vai um exemplo prático encontrando alunos que atendem a certos critérios:

python
students = {
    "Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
    "Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6},
    "Charlie": {"age": 21, "major": "Physics", "gpa": 3.9},
    "Diana": {"age": 20, "major": "Computer Science", "gpa": 3.5}
}
 
# Encontrar alunos de CS com GPA acima de 3.7
print("High-performing CS students:")
for name, info in students.items():
    if info["major"] == "Computer Science" and info["gpa"] >= 3.7:
        print(f"  {name} (GPA: {info['gpa']})")
 
# Output:
# High-performing CS students:
#   Alice (GPA: 3.8)

Dicionários são uma das estruturas de dados mais poderosas e versáteis do Python. Eles fornecem buscas rápidas, organização flexível e soluções elegantes para problemas comuns de programação. Conforme você continuar aprendendo Python, você vai encontrar dicionários em todo lugar — de configurações a processamento de dados e construção de aplicações complexas.

Os padrões que cobrimos neste capítulo — contagem, agrupamento, tabelas de consulta e transformação de dados — formam a base para trabalhar com dados estruturados em Python. No próximo capítulo, vamos explorar conjuntos, outro tipo de coleção que complementa dicionários ao trabalhar com dados únicos e não ordenados.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai