Curso de Python / Dicionários: chaves, valores e totais por categoria

Um dicionário encontra valores por meio de chaves. Ele é útil para registros com campos nomeados e para totais por categoria; ao consultar uma chave ausente, defina se essa ausência deve ser um erro ou se um valor padrão faz sentido.

Entenda as chaves e a substituição de valores

Um literal de dicionário usa chaves e pares no formato chave: valor. Atribuir novamente à mesma chave substitui o valor anterior, em vez de criar uma segunda ocorrência dessa chave. Execute este pequeno exemplo para observar a atualização:

expense = {"name": "lunch", "amount": 8000}
expense["amount"] = 9000
print(expense["amount"])

O resultado é 9000. O campo amount passou a ter o novo valor, e o dicionário continua tendo duas chaves: name e amount. A iteração preserva a ordem de inserção, mas não ordena automaticamente por nome ou por valor. Peça uma ordenação explícita quando essa for a regra de apresentação.

As chaves precisam ser hashable: devem fornecer um hash estável durante sua existência, e objetos iguais devem ter hashes iguais. Strings e inteiros são opções comuns; listas não são hashable. Uma tupla que contém uma lista também não pode ser chave. Essa condição é explicada no glossário oficial de Python e complementa a aula sobre tuplas e desempacotamento.

Chaves de texto diferenciam maiúsculas, minúsculas e espaços. "food", "Food" e " food " são três chaves diferentes. Decida se essas diferenças têm significado antes de normalizar os dados.

Calcule os totais de despesas por categoria

Salve o programa completo em UTF-8 como dicts.py, na pasta do projeto, e execute python dicts.py com o ambiente selecionado. No macOS ou Ubuntu, fora de um ambiente virtual, use python3 se esse for o comando disponível. Não são necessários pacotes nem arquivos externos.

expenses = [("food", 8000), ("transport", 2500), ("food", 3500)]
totals = {}
for category, amount in expenses:
    totals[category] = totals.get(category, 0) + amount
for category, total in sorted(totals.items()):
    print(category, total)
print(totals.get("books", 0))

A saída esperada é:

food 11500
transport 2500
0

A primeira repetição percorre os pares de categoria e valor. Para a primeira despesa de uma categoria, get(category, 0) fornece o zero inicial; depois, o programa soma o valor e atribui o resultado à chave. As duas despesas de food resultam em 11500, e transport fica com 2500.

Na segunda repetição, items() fornece os pares de chave e valor, e sorted() define uma ordem de saída pelas chaves desses pares. A última linha consulta a categoria ausente books com o padrão zero. Os textos em inglês são os dados de categoria usados no exemplo.

Escolha entre uma consulta obrigatória e um padrão

Em um dict comum, totals["books"] provoca KeyError quando a chave não existe. totals.get("books", 0) retorna 0 nesse caso, sem inserir a chave. A referência do tipo dict descreve as duas formas.

O padrão de get() é usado somente para uma chave ausente. Se a chave existir com o valor None, get("books", 0) retorna None, não zero. Também não verifica se um valor existente é numérico ou pertence à unidade esperada.

Um padrão zero é adequado para começar uma soma por categoria, mas pode esconder um registro inválido quando um campo é obrigatório. Para verificar a presença, use uma expressão como "amount" in expense. Não confunda ausência de um valor com um valor zero que foi informado corretamente.

Ao percorrer o dicionário diretamente, você recebe as chaves. Use values() para os valores ou items() para os pares. Uma cópia com dict.copy() é rasa: se um valor for uma lista, a cópia ainda pode compartilhar essa lista interna com o original.

Se o padrão de acumular a partir de zero aparecer muitas vezes, a biblioteca padrão oferece defaultdict(int). Ao acessar uma chave ausente com colchetes, ele cria o valor inicial; seu get(), porém, continua tendo o comportamento normal de consulta. Escolha a ferramenta sabendo se a consulta deve ou não modificar o dicionário.

Teste a normalização e as mudanças na coleção

  1. Troque somente 3500 por 4500 no último registro de expenses. Confira food 12500, mantendo transport 2500.
  2. Restaure o valor. Troque apenas a categoria do primeiro registro de food para Food. A saída terá grupos separados: Food 8000 e food 3500, além de transport 2500.
  3. Restaure a categoria e confirme novamente as três linhas originais. Se books for obrigatório em outra aplicação, confira sua presença, em vez de usar o zero como prova de que o dado existe.

Acrescentar ou remover chaves durante a iteração sobre o mesmo dicionário pode provocar RuntimeError. Reúna as chaves de interesse antes de alterar a coleção ou crie um novo dicionário com o resultado. Separe a etapa de identificar os registros da etapa de modificar a estrutura, para não depender de uma coleção cujo tamanho muda durante o percurso.