Domine o defaultdict em Python: Solucione KeyErrors com facilidade!

Foto do autor

By luis

Neste guia, você explorará o uso do `defaultdict`, um recurso do módulo `collections` do Python, projetado para aprimorar o tratamento de `KeyError`s ao manipular dicionários.

Em Python, dicionários são estruturas de dados versáteis, ideais para armazenar informações em pares de chave-valor. O acesso aos valores é feito utilizando as respectivas chaves.

Entretanto, em projetos Python que envolvem múltiplas modificações de dicionários durante a execução, é comum deparar-se com `KeyError`s. Existem diversas estratégias para lidar com esses erros.

Neste tutorial, você irá aprender sobre:

  • A natureza dos `KeyError`s e suas causas
  • Estratégias para gerenciar `KeyError`s
  • O uso do `defaultdict`, uma subclasse que herda as propriedades do dicionário, para gerenciar chaves ausentes de maneira eficaz

Vamos começar!

O Que São `KeyError`s em Python?

Ao definir dicionários em Python, atente-se para:

  • A singularidade das chaves: elas não podem se repetir.
  • A preferência por coleções imutáveis, como tuplas, ao usar iteráveis como chaves.

Uma chave só é válida se estiver presente no dicionário; caso contrário, ocorre um `KeyError`.

Considere o dicionário `livros_autores`, onde as chaves são títulos de livros e os valores, os nomes dos autores.

Experimente o código em um ambiente Python REPL para acompanhar o tutorial.

livros_autores = {
    'Trabalho Focado':'Cal Newport',
    'Hiperfoco':'Chris Bailey',
    'Giro':'Jenny Blake',
    'A Equação da Felicidade':'Neil Pasricha'
}

Acesse o nome do autor utilizando o título do livro como chave:

livros_autores['Hiperfoco']
'Chris Bailey'

Para obter todos os pares chave-valor, utilize o método `items()`:

for livro, autor in livros_autores.items():
    print(f"'{livro}' por {autor}")
'Trabalho Focado' por Cal Newport
'Hiperfoco' por Chris Bailey
'Giro' por Jenny Blake
'A Equação da Felicidade' por Neil Pasricha

Se você tentar acessar um valor usando uma chave inexistente, o Python gera um `KeyError`. Isso ocorre ao tentar acessar os valores de chaves inexistentes, como ‘Garra’ e ‘chave-inexistente’.

livros_autores['Garra']
Traceback (most recent call last):
  File "", line 1, in 
KeyError: 'Garra'
livros_autores['chave-inexistente']
Traceback (most recent call last):
  File "", line 1, in 
KeyError: 'chave-inexistente'

Como, então, podemos lidar com `KeyError`s em Python?

A seguir, exploraremos algumas estratégias.

Como Gerenciar `KeyError`s em Python

Vamos explorar as abordagens para tratar `KeyError`s usando:

  • Instruções condicionais `if-else`
  • Blocos `try-except`
  • O método `.get()` de dicionários

#1. Usando Instruções Condicionais `If-Else`

Uma forma simples de gerenciar `KeyError`s é utilizando as estruturas condicionais `if-else`.

Em Python, a sintaxe geral de `if-else` é:

if condição:
    # execute este bloco
else:
    # execute este outro bloco
  • Se `condição` for `True`, as instruções dentro do bloco `if` são executadas.
  • Caso contrário, as instruções no bloco `else` são executadas.

Aqui, a condição é verificar a existência da chave no dicionário. Se a chave existir, o operador `in` retorna `True` e o valor correspondente é impresso.

chave = 'A Equação da Felicidade'
if chave in livros_autores:
    print(livros_autores[chave])
else:
    print('Desculpe, esta chave não existe!')
# Saída
# Neil Pasricha

Se a chave não estiver presente, `in` retorna `False`, e o bloco `else` é executado, imprimindo uma mensagem de aviso.

chave = 'chave-inexistente'
if chave in livros_autores:
    print(livros_autores[chave])
else:
    print('Desculpe, esta chave não existe!')
# Saída
# Desculpe, esta chave não existe!

#2. Usando Instruções `Try-Except`

Outra abordagem comum para o tratamento de `KeyError`s é o uso de blocos `try-except`.

Observe o seguinte trecho de código:

chave = 'chave-inexistente'
try:
    print(livros_autores[chave])
except KeyError:
    print('Desculpe, esta chave não existe!')
  • O bloco `try` tenta acessar o valor associado à chave.
  • Se a chave não existir, um `KeyError` é gerado e tratado dentro do bloco `except`.

#3. Usando o Método `.get()`

O método `.get()` embutido em dicionários é uma ferramenta útil para lidar com chaves faltantes.

A sintaxe é: `dicionario.get(chave, valor_padrao)`, onde `dicionario` é um objeto dicionário válido.

– Se a `chave` estiver presente, o método retorna o valor correspondente.
– Caso contrário, retorna `valor_padrao`.

Neste exemplo, `chaves` é uma lista de chaves que queremos acessar. Iteramos sobre essa lista para obter os valores do dicionário `livros_autores`.

Usamos `.get()` com ‘Não existe’ como valor padrão.

chaves = ['Garra', 'Hiperfoco', 'Faça Tempo', 'Trabalho Focado']
for chave in chaves:
    print(livros_autores.get(chave, 'Não existe'))

No código acima:

  • Para chaves existentes em `livros_autores`, o método `.get()` retorna os valores associados.
  • Quando as chaves não existem, como ‘Garra’ e ‘Faça Tempo’, `.get()` retorna o valor padrão ‘Não existe’.
# Saída
Não existe
Chris Bailey
Não existe
Cal Newport

Todos os métodos acima tratam erros de chave. No entanto, são detalhados e exigem tratamento explícito de chaves ausentes. O uso de um `defaultdict` pode simplificar esse processo.

`Defaultdict` em Python

O `defaultdict` é uma subclasse do tipo `dict`, herdando seu comportamento, mas com tratamento nativo para chaves ausentes.

É um tipo de dado de contêiner encontrado no módulo `collections` da biblioteca padrão do Python.

É preciso importá-lo:

from collections import defaultdict

A sintaxe geral para usar `defaultdict` é:

defaultdict(função_padrão)

É possível especificar funções como `int`, `float` ou `list` como o atributo `função_padrão`. Se não for fornecido, o padrão é `None`.

Quando uma chave não é encontrada, o método `__missing__()` é acionado, inferindo o valor padrão de `função_padrão` e o retornando.

Em resumo:

  • Um `defaultdict` retorna o valor padrão quando uma chave não existe.
  • Ele também adiciona o par chave-valor padrão ao dicionário, o qual pode ser modificado.

Exemplos de `Defaultdict` em Python

A seguir, vamos codificar exemplos para entender o funcionamento do `defaultdict`.

`Defaultdict` com Valor Inteiro Padrão

Primeiro, importe `defaultdict`:

from collections import defaultdict
import random

Criamos um `defaultdict` para preços:

preços = defaultdict(int)

Preenchemos o dicionário utilizando nomes de frutas como chaves e amostramos aleatoriamente valores de uma lista para obter os preços.

lista_preços = [10, 23, 12, 19, 5]
frutas = ['maçã', 'morango', 'romã', 'mirtilo']

for fruta in frutas:
    preços[fruta] = random.choice(lista_preços)

Vejamos os pares chave-valor em `preços`:

print(preços.items())
dict_items([('maçã', 12), ('morango', 10), ('romã', 19), ('mirtilo', 5)])

Assim como um dicionário normal, podemos acessar os valores usando as chaves:

preços['maçã']
# 12

Ao tentar acessar o preço de uma fruta inexistente, como ‘laranja’, ele retorna o valor padrão, zero.

preços['laranja']
# 0

Se imprimirmos o dicionário, veremos que a chave ‘laranja’ foi adicionada com o valor padrão zero.

print(preços.items())
dict_items([('maçã', 12), ('morango', 10), ('romã', 19), ('mirtilo', 5), ('laranja', 0)])

`Defaultdict` com `List` como Valor Padrão

Definimos `alunos_cursos` como um `defaultdict` de listas, onde os nomes dos cursos são as chaves e os valores são listas de alunos matriculados em cada curso.

from collections import defaultdict
alunos_cursos = defaultdict(list)

Ao tentar acessar a lista de alunos de ‘Economia’, `defaultdict` retorna uma lista vazia, sem erros de chave!

alunos_cursos['Economia']
# []

Agora, temos uma lista vazia associada ao curso de ‘Economia’, à qual podemos adicionar elementos usando o método `.append()`.

alunos_cursos['Economia'].append('Alex')

Uma entrada foi criada para ‘Economia’ no `defaultdict` `alunos_cursos`.

print(alunos_cursos)
defaultdict(, {'Economia': ['Alex']})

Podemos adicionar mais alunos e criar novos cursos:

alunos_cursos['Economia'].append('Bob')
alunos_cursos['Matemática'].append('Laura')
print(alunos_cursos)
defaultdict(, {'Economia': ['Alex', 'Bob'], 'Matemática': ['Laura']})

Conclusão

Espero que este guia tenha esclarecido o uso do `defaultdict` em Python. Após praticar os exemplos, você pode optar por usar essa estrutura em seus projetos sempre que necessário.

Segue um resumo do que aprendemos neste guia:

  • `KeyError`s são comuns ao trabalhar com dicionários.
  • Para gerenciar `KeyError`s, utilize métodos como condicionais, blocos `try-except` ou o método `.get()`. O tipo de dados `defaultdict` do módulo `collections` simplifica o tratamento desses erros.
  • Use `defaultdict(função_padrão)`, onde `função_padrão` é um objeto callable válido.
  • Se uma chave não estiver presente no `defaultdict`, o valor padrão (inferido de `função_padrão`) e a chave são adicionados ao `defaultdict`.

Em seguida, confira o tutorial sobre a função `map` do Python.