Python: Trabalhando com dicionários

Python: Trabalhando com dicionários
Yan Orestes
Yan Orestes

Compartilhe

Estou programando um sistema de agenda de contatos telefônicos em Python. Para isso, preciso armazenar os números dos contatos. A princípio, podemos pensar em usar uma lista:

telefones = ['1234-5678', '9999-9999', '8765-4321', '8877-7788']

Tudo bem, temos os números de telefone armazenados. Mas… qual o sentido de termos uma lista de números soltos? De quem é o número que está na segunda posição?

Precisamos, de algum modo, conectar os telefones a seus respectivos contatos. Já conhecemos um tipo que pode nos ajudar com isso a tupla:

contato = ('Yan', '1234-5678')

Para não precisarmos de uma variável para cada contato, podemos colocá-los direto em uma lista de contatos:

contatos_lista = [('Yan', '1234-5678'), ('Pedro', '9999-9999'),
                    ('Ana', '8765-4321'), ('Marina', '8877-7788')]

Ok! Se quisermos acessar o número de telefone da Marina, podemos fazer:

print(contatos_lista[3][1])

E o resultado:

8877-7788

Conseguimos! Agora, o número do Pedro: … Mas espera, qual é mesmo a posição do Pedro na nossa lista de contatos?

Repare que do modo como está, mal faz diferença ter os nomes dos contatos salvos, porque só conseguimos acessar cada contato pela sua posição na lista. Será que não há um jeito melhor?

Mapeando contatos com um dicionário

Até agora temos uma lista de contatos em que, ao menos, cada contato tem seu nome e telefone conectados. Entretanto, por enquanto, só conseguimos acessar um contato individualmente pela sua posição na lista, e não pelo seu próprio nome.

O ideal seria mapear o nome de cada contato com seu telefone, evitando outros problemas.

Por exemplo, podemos falar que o contato Yan tem o número de telefone 1234-5678. Assim, quando quisermos saber qual o n de telefone do Yan, basta ir até o seu nome. Dessa forma, não precisamos decorar qual a posição na lista que o telefone se encontra, basta sabermos seu nome de contato.

Veja que, nesse caso, estamos criando uma espécie de dicionário, parecido com os dicionários de língua portuguesa, ou inglesa. Nesses dicionários, temos uma chave que é a palavra que estamos a buscar, que no nosso caso é o nome de contato.

Quando achamos essa palavra, podemos ver o seu significado, isto é, o valor daquela palavra na língua, que no nosso caso, é o número de telefone.

Esse tipo de estrutura é muito utilizado em diversas linguagens de programação (mas normalmente tem outro nome, como array associativo. Com ela, conseguimos ter um comportamento parecido com o de dicionários.

Bem, vamos falar para o Python criar um desses dicionários para a gente. No Python, usamos chaves ({}) para construir nossos dicionários. Neste caso, falamos para o Python, que a chave 'Yan' possuí (:) o valor 1234-5678 como seu telefone:

contatos = {'Yan': '1234-5678'}
print(type(contatos))

E olha o tipo da variável contatos que acabamos de criar:

<class 'dict'>

dict - de fato um dicionário. Mas será que vamos ter que redigitar todos os dados de contatos que já colocamos em nossa lista de contatos? Também podemos criar um dicionário usando sua função construtora dict() e passando, como parâmetro, uma lista de tuplas, como em nosso caso:

contatos_lista = [('Yan', '1234-5678'), ('Pedro', '9999-9999'),
                    ('Ana', '8765-4321'), ('Marina', '8877-7788')]

contatos = dict(contatos_lista)
print(contatos)

E agora:

{'Yan': '1234-5678', 'Pedro': '9999-9999', 'Ana': '8765-4321',
  'Marina': '8877-7788'}

Certo, temos nossa estrutura pronta! Mas espera aí, o nosso dicionário não está ordenado em ordem alfabética, ele não tem ordem nenhuma… Como podemos acessar seus itens?

Banner promocional da Alura, com um design futurista em tons de azul, apresentando dois blocos de texto, no qual o bloco esquerdo tem os dizeres:

Acessando os itens de um dicionário

Podemos acessar os valores dele de forma similar a como acessamos os valores de uma lista, por exemplo, com a diferença de que usamos as chaves que definimos no lugar dos índices numéricos:

print(contatos['Ana'])

E o resultado:

8765-4321

Tudo bem! Até que, depois de um tempo, quis ver se eu encontrava o telefone de um velho amigo João. Fiz o seguinte:

print(contatos['João'])

Mas olha o que apareceu na tela:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'João'

Hum… uma exceção de tipo KeyError indicando que a chave 'João' não foi encontrada. Mas é um pouco estranho imprimir toda essa mensagem para o usuário, não é? Pode ser confuso... Será que não podemos substituir isso?

Os dicionários possuem um método específico para busca de valores, o get(), no qual podemos passar como parâmetros a chave que queremos e um valor padrão para retornar caso essa chave não seja encontrada:

print(contatos.get('Yan', 'Contato não encontrado'))
print(contatos.get('João', 'Contato não encontrado'))

E o resultado:

1234-5678
Contato não encontrado

Muito melhor agora!

Também podemos verificar se um contato está em nosso dicionário através da palavra chave in:

print('Yan' in contatos)

E a resposta:

True

Como esperado!

Esses dias, achei um número solto aqui e quis verificar se ele estava em minha agenda:

print('9999-9999' in contatos)

Dessa vez, olha o resultado:

False

Ué! Mas esse número está sim na agenda, é o número do Pedro! Por que será que o resultado foi False, então?

Acontece que o in, usado dessa forma, verifica apenas as chaves do dicionário, não os valores. Para obtermos valores, podemos usar o método values():

print('9999-9999' in contatos.values())

E a resposta:

True

Agora sim! Temos nossa estrutura de mapeamento e já conseguimos visualizar os dados dela. Mas e agora, o que mais conseguimos fazer?

Adicionando valores ao dicionário

Encontrei meu amigo João e, finalmente, decidi adicionar o número dele na minha agenda. Mas… como posso fazer isso com nosso dicionário de contatos? Fui tentar usar um método append(), como nas listas, e olha o que apareceu:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'append'

Esse método não existe… Ainda tentei criar um outro dicionário e fazer uma soma, mas o resultado foi esse:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

Também não funciona! A sintaxe de adicionar um item em um dicionário é um pouco diferente de que em outros tipos do Python, mas também bastante objetiva. Por exemplo, se queremos adicionar o João no nosso dicionário de contatos, basta atribuir seu telefone na chave 'João':

contatos['João'] = '8887-7778'
print(contatos)

Agora olha como está o dicionário:

{'Yan': '1234-5678', 'Pedro': '9999-9999', 'Ana': '8765-4321',
  'Marina': '8877-7788', 'João': '8887-7778'}

Deu certo!

Removendo itens do dicionário

Infelizmente, minha amiga Marina perdeu o celular e, consequentemente, não era mais dona do número salvo em meu dicionário de contatos. Precisamos, agora, apagar o item que corresponde a ela. Mas como?

Uma maneira simples é usando o statement del, dessa forma:

del contatos['Marina']
print(contatos)

E agora, nosso dicionário:

{'Yan': '1234-5678', 'Pedro': '9999-9999', 'Ana': '8765-4321',
  'João': '8887-7778'}

Certo! Mas e se tentarmos remover um item que não existe?

del contatos['Catarina']

Olha o que acontece:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Catarina'

Um KeyError, como aquele que obtivemos ao tentar pegar um item que não existia! Para evitar essa exceção, também temos um método de dicionário que pode nos ajudar - o pop().

O método pop(), além de remover o elemento com a chave especificada do dicionário, nos retorna o valor desse elemento. Também podemos definir um valor padrão de retorno, para caso a chave não seja encontrada:

contatos = {'Yan': '1234-5678', 'Pedro': '9999-9999', 'Ana': '8765-4321',
            'Marina': '8877-7788', 'João': '8887-7778'}

print(contatos.pop('Marina', 'Contato não encontrado'))
print(contatos.pop('Catarina', 'Contato não encontrado'))
print()
print(contatos)

E o resultado:

8877-7788
Contato não encontrado

{'Yan': '1234-5678', 'Pedro': '9999-9999', 'Ana': '8765-4321',
  'João': '8887-7778'}

Tudo certo, agora! Uma pena que tenho poucos contatos na agenda...

Juntando dois dicionários

Pedi para meu amigo Pedro me ajudar a aumentar minha agenda, adicionando mais alguns amigos como contatos. Ele me passou a agenda dele:

contatos_do_pedro = {'Yan': '1234-5678', 'Fernando':'4345-5434',
                        'Luiza':'4567-7654'}

Mas e aí? Como adicionamos todos esses contatos em minha agenda? Podemos tentar fazer um loop passando pelos contatos do Pedro e os adicionando um a um:

meus_contatos = {'Yan': '1234-5678', 'Pedro': '9999-9999',
                    'Ana': '8765-4321', 'João': '8887-7778'}

contatos_do_pedro = {'Yan': '1234-5678', 'Fernando': '4345-5434',
                        'Luiza': '4567-7654'}

for nome in contatos_do_pedro:
    meus_contatos[nome] = contatos_do_pedro[nome]

print(meus_contatos)

Agora vamos ver meu dicionário:

{'Yan': '1234-5678', 'Pedro': '9999-9999', 'Ana': '8765-4321',
  'João': '8887-7778', 'Fernando': '4345-5434', 'Luiza': '4567-7654'}

Certo, funcionou! Mas será que não tem um jeito mais simples de fazermos isso, evitando o laço for?

Repare que queremos juntar nossos dois dicionários em um só - o primeiro. Queremos atualizar o primeiro dicionário para que contenha também os valores do segundo. Para isso, temos um método próprio - o update(). Podemos substituir o loop pela chamada do método:

meus_contatos.update(contatos_pedro)
print(meus_contatos)

E o resultado continua certo, com um código mais sucinto!

O problema do prefixo 9 e compreensões de dicionário

Fui tentar ligar para meus amigos e em todas as minhas tentativas recebi a mensagem de número não existente. Mas ué, por quê? Eu sei que os números estão corretos…

Pesquisei e descobri que os números de celular precisam ter o prefixo 9 hoje em dia, e eu tinha esquecido de atualizar isso! Mas e agora? Será que vou precisar usar o método update() para mudar todos os valores? Não faz muito sentido…

O ideal seria ter uma lógica mais objetiva, que simplesmente copiasse todo o dicionário de contatos, mas adicionasse um 9 a cada valor. A princípio, uma ideia é fazer isso com um for, o que deve funcionar, mas será que não tem forma mais simples?

Mais sintático e elegante que um loop for tradicional, entretanto, é o que podemos fazer com o que já conhecemos da sintaxe de compreensão de lista, mas dessa vez aplicado aos dicionários compreensão de dicionário. A ideia é a mesma e a sintaxe parecida:

meus_contatos_novo = {nome: '9' + meus_contatos[nome] for nome in meus_contatos}
print(meus_contatos_novo)

E o resultado:

{'Yan': '91234-5678', 'Pedro': '99999-9999', 'Ana': '98765-4321',
  'João': '98887-7778', 'Fernando': '94345-5434', 'Luiza': '94567-7654'}

Conseguimos! Agora já conseguimos manipular bem nossa agenda!

Para saber mais

Dicionários são mutáveis

Um detalhe que talvez você tenha percebido até aqui é que ao longo do post, normalmente modificamos (seja adicionando, removendo, ou atualizando de fato) o dicionário no próprio local, sem a necessidade de criar um outro objeto.

Isso se dá porque os dicionários, assim como as listas, são mutáveis. É importante ter noção disso, para não nos atrapalharmos nas possíveis complicações que a diferença de mutabilidade implica nos objetos no Python.

Views vs. listas

Usamos o método values() para verificar se o dicionário tinha um valor específico, mas não chegamos a ver o que ele retorna. Vamos testar:

valores = meus_contatos_novo.values()
print(valores)

Aqui chegamos em um ponto interessante. Ao rodarmos esse código no Python 2, olha o que aparece

['91234-5678', '99999-9999', '98765-4321', '98887-7778',
  '94345-5434', '94567-7654']

Uma lista contendo os valores, certo. Agora, no interpretador do Python 3:

dict_values(['91234-5678', '99999-9999', '98765-4321',
                '98887-7778', '94345-5434', '94567-7654'])

O resultado é parecido, mas de outro tipo dict_values. O que é isso? Essa classe é o que chamamos de view de dicionário, que nada mais é que uma janela para os valores do dicionário.

Views geralmente são vantajosos, comparados às listas, justamente porque são apenas uma abertura, não uma estrutura em si. Assim, eles estão sempre atualizados:

valores = meus_contatos_novo.values()
print(valores)

meus_contatos_novo['Yan'] = '91122-3344'
print(valores)

No interpretador do Python 2:

['91234-5678', '99999-9999', '98765-4321', '98887-7778',
  '94345-5434', '94567-7654']

A lista ficou como a anterior. Já no do Python 3:

dict_values(['911122-3344', '99999-9999', '98765-4321',
                '98887-7778', '94345-5434', '94567-7654'])

A variável valores atualizou junto! Além dessa vantagem, também economizamos memória com as views, já que elas sempre ocupam o mesmo espaço, como uma janela:

print(valores.__sizeof__())

Com a lista no Python 2, o resultado:

112

Agora, com a view no Python 3:

24

Bem menos!

O tipo de mapeamento no Python

Tuplas e listas podem nos ajudar bastante a organizar nossos dados nos programas, mas, às vezes, precisamos de um tipo mais direto e específico para mapeamento com base em chave e valor.

Para essa necessidade, no Python temos o tipo dicionário, que é mutável e nos provê diversos métodos para facilitar o manipulamento dos dados nessa estrutura.

Nesse post, aprendemos não só a criar um dicionário, mas a acessar itens dentro dele, adicionar outros, remover (veja também esse assunto Append ou Extend? adicionando elementos na lista com Python), e até juntar um dicionário com outro. Outra funcionalidade legal que pudemos explorar é a de compreensão de dicionário, que nos permite uma criação de dicionário poderosa com sintaxe mais simples.

Gostou do conteúdo? Esse novo tipo pode te ajudar bastante como desenvolvedor Python! Se quiser se aprofundar mais na linguagem, dê uma olhada na nossa formação Python para web e continue estudando! Agora se você já trabalha com Python e gostaria de algo mais avançado, dê uma olhada nesse curso de Python para Data Science.

Veja outros artigos sobre Programação