Python Percorrendo um Dicionário
Até aqui, você pegava um valor do dicionário de cada vez, pelo nome: precos["leite"]. Mas e para imprimir a tabela de preços inteira, somar todo o estoque ou achar o produto mais caro? Aí é preciso passar por todos os pares, um por um, com o laço for (aula for). Esta aula mostra as três formas de percorrer um dicionário: pelas chaves, pelos valores e pelos pares.
- O que o
fordevolve quando você percorre um dicionário direto. - Os três métodos
keys(),values()eitems(), e quando usar cada um. - A desempacotar chave e valor no
for:for produto, preco in precos.items():. - A somar, achar o maior e filtrar valores, e a percorrer em ordem alfabética.
- Os erros reais: mudar o tamanho do dicionário no meio do
for, esquecer oitems()e esquecer os parênteses.
A ideia: ler a tabela de preços em voz alta
A Ana precisa ditar a tabela de preços para a nova funcionária. Ela pode ler de três jeitos: só os nomes dos produtos ("pão, leite, café"), só os preços ("cinquenta centavos, quatro e setenta e cinco…") ou as linhas inteiras ("pão, cinquenta centavos; leite, quatro e setenta e cinco…"). Cada jeito serve para uma coisa. Em Python, os três jeitos são keys(), values() e items().
| Quero ler… | Método | A cada volta do for, recebo |
|---|---|---|
| Os nomes | precos.keys() (ou só precos) | "pão" |
| Os preços | precos.values() | 0.5 |
| As linhas inteiras | precos.items() | ("pão", 0.5) |
1. O for direto: passa pelas chaves
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
for produto in precos:
print(produto)
$ python3 p_for.py pão leite café
Quando você escreve for produto in precos:, a variável produto recebe, a cada volta, uma chave, na ordem em que as chaves entraram no dicionário. Os valores não aparecem. Para vê-los, use a chave para buscar o valor, como você já sabe:
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
for produto in precos:
print(produto, "custa", precos[produto])
$ python3 p_for_valor.py pão custa 0.5 leite custa 4.75 café custa 18.9
Funciona, mas a cada volta o Python precisa procurar a chave de novo. Há um jeito mais direto: o items(), na seção 3.
2. keys(), values() e items()
Veja o que cada método devolve, antes de usá-lo num for:
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
print(precos.keys())
print(precos.values())
print(precos.items())
print(list(precos.keys()))
$ python3 p_metodos.py
dict_keys(['pão', 'leite', 'café'])
dict_values([0.5, 4.75, 18.9])
dict_items([('pão', 0.5), ('leite', 4.75), ('café', 18.9)])
['pão', 'leite', 'café']
- precos.keys()
- "Chaves": todas as chaves. O Python mostra como
dict_keys([…]), um tipo especial que parece uma lista. - precos.values()
- "Valores": todos os valores, na mesma ordem.
- precos.items()
- "Itens": todos os pares, cada um como uma tupla
(chave, valor). - list(precos.keys())
- Para ter uma lista de verdade (com índice,
appende tudo), converta comlist().
Esses três objetos se chamam visões (views): não são uma cópia, são uma janela para o dicionário. Se o dicionário muda, a visão mostra a mudança:
precos = {"pão": 0.50}
chaves = precos.keys()
print(chaves)
precos["leite"] = 4.75
print(chaves)
$ python3 p_vivo.py dict_keys(['pão']) dict_keys(['pão', 'leite'])
A variável chaves não foi tocada, e mesmo assim agora mostra o leite.
3. items(): chave e valor juntos
Esta é a forma mais usada. Como cada item é uma tupla (chave, valor), você a desempacota direto no for, em duas variáveis:
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
for produto, preco in precos.items():
print(f"{produto:<6} R$ {preco:6.2f}")
$ python3 p_items.py pão R$ 0.50 leite R$ 4.75 café R$ 18.90
- for produto, preco in precos.items():
- A cada volta, o
items()entrega uma tupla, como("pão", 0.5), e oforfazproduto, preco = ("pão", 0.5). - produto, preco
- Os nomes são escolha sua. Escolha nomes que digam o que é a chave e o que é o valor:
nome, telefone,aluno, nota. Evitek, vno começo. - {produto:<6}
- Formatação da f-string: o nome ocupa 6 casas, alinhado à esquerda, para a tabela ficar reta.
- {preco:6.2f}
- O preço com 2 casas decimais, ocupando 6 casas, alinhado à direita (o detalhe está em Formatação).
Missão: o programa mostra só os nomes. Mude o for para usar items() e mostre cada linha como leite: R$ 4.75 (com duas casas decimais).
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
for produto in precos:
print(produto)
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
for produto, preco in precos.items():
print(f"{produto}: R$ {preco:.2f}")
4. values(): quando só os valores importam
Para contas sobre os valores (somar o estoque, achar o maior), o nome das chaves nem interessa. Use values(), com as funções sum e max:
estoque = {"pão": 40, "leite": 12, "café": 8}
for quantidade in estoque.values():
print(quantidade)
print("Total de itens:", sum(estoque.values()))
print("Maior estoque:", max(estoque.values()))
print(12 in estoque.values())
$ python3 p_values.py 40 12 8 Total de itens: 60 Maior estoque: 40 True
A última linha responde à pergunta que o in sozinho não respondia na aula Dicionários: 12 in estoque procura nas chaves; 12 in estoque.values() procura nos valores.
Missão: troque o 0 pela soma de todos os valores do estoque, usando sum() e values().
estoque = {"pão": 40, "leite": 12, "café": 8}
print("Total de itens:", 0)
estoque = {"pão": 40, "leite": 12, "café": 8}
print("Total de itens:", sum(estoque.values()))
5. Receitas do dia a dia
Filtrar: só o que cumpre uma condição
Quais produtos têm menos de 5 unidades e precisam ser repostos?
estoque = {"pão": 40, "leite": 3, "café": 8, "bolo": 2}
for produto, quantidade in estoque.items():
if quantidade < 5:
print("Repor:", produto)
$ python3 p_filtro.py Repor: leite Repor: bolo
Achar a chave do maior valor
O max(precos.values()) diz qual é o maior preço, mas não de qual produto. Para saber os dois, guarde o maior visto até agora enquanto percorre:
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90, "bolo": 12.00}
mais_caro = None
maior = 0
for produto, preco in precos.items():
if preco > maior:
maior = preco
mais_caro = produto
print(f"O mais caro é {mais_caro}: R$ {maior:.2f}")
$ python3 p_maior.py O mais caro é café: R$ 18.90
Em ordem alfabética
O for segue a ordem de inserção. Para a ordem alfabética das chaves, percorra o sorted() do dicionário, que devolve uma lista com as chaves em ordem:
agenda = {"Caio": "99999-0003", "Ana": "99999-0001", "Bia": "99999-0002"}
for nome in sorted(agenda):
print(nome, agenda[nome])
$ python3 p_sorted.py Ana 99999-0001 Bia 99999-0002 Caio 99999-0003
Numerar as linhas
Com enumerate (aula enumerate() e zip()), cada chave ganha um número:
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
for numero, produto in enumerate(precos, start=1):
print(numero, produto)
$ python3 p_enumerate.py 1 pão 2 leite 3 café
Mudar os valores durante o for
Trocar o valor de chaves que já existem, enquanto percorre, é permitido. Um aumento de 10% em tudo:
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
for produto in precos:
precos[produto] = round(precos[produto] * 1.10, 2)
print(precos)
$ python3 p_alterar_valor.py
{'pão': 0.55, 'leite': 5.23, 'café': 20.79}
O que não pode é acrescentar ou remover chaves no meio do for; veja a seguir.
Formas diferentes de percorrer
| Forma | A cada volta | Quando usar |
|---|---|---|
for k in d: | Uma chave | Quando só as chaves importam. É o mesmo que for k in d.keys():, mais curto |
for k in d.keys(): | Uma chave | Igual ao de cima; alguns preferem por deixar claro que são as chaves |
for v in d.values(): | Um valor | Somas, máximos, contagens dos valores |
for k, v in d.items(): | Chave e valor | Quase sempre que você precisa dos dois. Prefira a for k in d: … d[k] |
for k in sorted(d): | Uma chave, em ordem | Quando a saída precisa estar em ordem alfabética ou crescente |
Quando dá errado
for
A Ana quer tirar do estoque os produtos que zeraram:
estoque = {"pão": 40, "leite": 0, "café": 8, "bolo": 0}
for produto in estoque:
if estoque[produto] == 0:
del estoque[produto]
$ python3 p_runtime.py
Traceback (most recent call last):
File "/home/ana/p_runtime.py", line 2, in <module>
for produto in estoque:
^^^^^^^
RuntimeError: dictionary changed size during iteration
- line 2 … for produto in estoque:
- Repare que o erro aponta para a linha do
for, e não para odel. Odelfuncionou; foi ofor, ao tentar pegar a próxima chave, que percebeu a mudança. - RuntimeError
- "Erro de execução": um problema que só aparece com o programa rodando.
- dictionary changed size during iteration
- "O dicionário mudou de tamanho durante a iteração" (durante o
for). Oforestava andando pelo dicionário e o chão mudou debaixo dele.
Como consertar: percorra uma cópia das chaves, feita com list(). O for anda pela lista, que não muda, e você apaga à vontade no dicionário:
estoque = {"pão": 40, "leite": 0, "café": 8, "bolo": 0}
for produto in list(estoque):
if estoque[produto] == 0:
del estoque[produto]
print(estoque)
$ python3 p_runtime_ok.py
{'pão': 40, 'café': 8}
Outro jeito é criar um dicionário novo só com o que fica, com a dict comprehension.
items()
precos = {"pão": 0.50, "leite": 4.75}
for produto, preco in precos:
print(produto, preco)
$ python3 p_sem_items.py
Traceback (most recent call last):
File "/home/ana/p_sem_items.py", line 2, in <module>
for produto, preco in precos:
^^^^^^^^^^^^^^
ValueError: too many values to unpack (expected 2)
Este engana. Sem items(), o for entrega só as chaves. A primeira é o texto "pão", e o Python tenta desempacotar esse texto em produto, preco, letra por letra: são 3 letras para 2 variáveis, "valores demais para desempacotar". (Se a chave tivesse exatamente 2 letras, nem daria erro: produto e preco receberiam uma letra cada.) Como consertar: for produto, preco in precos.items():.
items
precos = {"pão": 0.50, "leite": 4.75}
for produto, preco in precos.items:
print(produto, preco)
$ python3 p_items_sem_parenteses.py
Traceback (most recent call last):
File "/home/ana/p_items_sem_parenteses.py", line 2, in <module>
for produto, preco in precos.items:
^^^^^^^^^^^^
TypeError: 'builtin_function_or_method' object is not iterable
Sem os parênteses, precos.items é o próprio método, e não o resultado dele: é como dar ao for o nome da receita em vez do bolo. "Um objeto builtin_function_or_method (uma função embutida) não é iterável", ou seja, não dá para percorrer. Como consertar: precos.items(), com os parênteses que chamam o método.
Referência rápida
| Escrita | O que faz |
|---|---|
for k in d: | Percorre as chaves, na ordem de inserção |
d.keys() | Visão das chaves |
d.values() | Visão dos valores |
d.items() | Visão dos pares (chave, valor) |
for k, v in d.items(): | Percorre chave e valor juntos |
sum(d.values()), max(d.values()) | Soma e maior dos valores |
x in d.values() | Procura nos valores (o x in d procura nas chaves) |
for k in sorted(d): | Percorre as chaves em ordem |
for k in list(d): | Percorre uma cópia das chaves (para poder remover) |
list(d.keys()) | Lista de verdade com as chaves |
| Mensagem | Causa provável |
|---|---|
RuntimeError: dictionary changed size during iteration | Acrescentou ou removeu chave dentro do for: percorra list(d) |
ValueError: too many values to unpack (expected 2) no for | Faltou o .items(): o for está desempacotando a chave |
TypeError: 'builtin_function_or_method' object is not iterable | Faltaram os parênteses: items(), values(), keys() |
Exercícios
1. Os pares
Complete o método que entrega chave e valor juntos:
2. Só os valores
Complete para somar todas as notas do dicionário notas:
3. Em ordem
Complete a função que faz o for passar pelas chaves em ordem alfabética:
4. Em for x in {"a": 1, "b": 2}:, o que x recebe?
5. Qual destas linhas dá RuntimeError: dictionary changed size during iteration?
6. Com estoque = {"pão": 40, "leite": 12}, como perguntar se algum produto tem exatamente 12 unidades?
Missão final: o caixa da padaria
Missão: o carrinho diz quantas unidades de cada produto o cliente levou, e precos diz quanto custa cada um. Percorra o carrinho com items(), somando em total o preço de cada produto vezes a quantidade, para o programa mostrar Total: R$ 14.50.
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
carrinho = {"pão": 10, "leite": 2}
total = 0
# percorra o carrinho aqui
print(f"Total: R$ {total:.2f}")
precos = {"pão": 0.50, "leite": 4.75, "café": 18.90}
carrinho = {"pão": 10, "leite": 2}
total = 0
for produto, quantidade in carrinho.items():
total += precos[produto] * quantidade
print(f"Total: R$ {total:.2f}")
Resumo
for k in d:passa pelas chaves, na ordem de inserção.keys(),values()eitems()dão as chaves, os valores e os pares; são "visões" que acompanham as mudanças do dicionário.for chave, valor in d.items():é a forma mais usada: desempacota cada par em duas variáveis.sum(d.values()),max(d.values())ex in d.values()trabalham com os valores;sorted(d)dá as chaves em ordem.- Trocar valores no
forpode; acrescentar ou remover chaves dáRuntimeError: percorralist(d). - Sem
.items(), ofortenta desempacotar a chave; sem os parênteses, tenta percorrer o próprio método.