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.

Você vai aprender
  • O que o for devolve quando você percorre um dicionário direto.
  • Os três métodos keys(), values() e items(), 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 o items() 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étodoA cada volta do for, recebo
Os nomesprecos.keys() (ou só precos)"pão"
Os preçosprecos.values()0.5
As linhas inteirasprecos.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, append e tudo), converta com list().

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 o for faz produto, 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. Evite k, v no 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

FormaA cada voltaQuando usar
for k in d:Uma chaveQuando só as chaves importam. É o mesmo que for k in d.keys():, mais curto
for k in d.keys():Uma chaveIgual ao de cima; alguns preferem por deixar claro que são as chaves
for v in d.values():Um valorSomas, máximos, contagens dos valores
for k, v in d.items():Chave e valorQuase sempre que você precisa dos dois. Prefira a for k in d: … d[k]
for k in sorted(d):Uma chave, em ordemQuando a saída precisa estar em ordem alfabética ou crescente

Quando dá errado

Remover chaves no meio do 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 o del. O del funcionou; foi o for, 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). O for estava 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.

Esquecer o 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():.

Esquecer os parênteses do 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

EscritaO 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
MensagemCausa provável
RuntimeError: dictionary changed size during iterationAcrescentou ou removeu chave dentro do for: percorra list(d)
ValueError: too many values to unpack (expected 2) no forFaltou o .items(): o for está desempacotando a chave
TypeError: 'builtin_function_or_method' object is not iterableFaltaram os parênteses: items(), values(), keys()

Exercícios

1. Os pares

Complete o método que entrega chave e valor juntos:

for nome, nota in notas.():

2. Só os valores

Complete para somar todas as notas do dicionário notas:

total = sum(notas.)

3. Em ordem

Complete a função que faz o for passar pelas chaves em ordem alfabética:

for nome in (agenda):

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() e items() 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()) e x in d.values() trabalham com os valores; sorted(d) dá as chaves em ordem.
  • Trocar valores no for pode; acrescentar ou remover chaves dá RuntimeError: percorra list(d).
  • Sem .items(), o for tenta desempacotar a chave; sem os parênteses, tenta percorrer o próprio método.