A classe dict do Python disponibiliza muitos métodos úteis para trabalhar com dicionários.
Alguns foram apresentados no conceito de dicts.
Aqui abordamos mais alguns, juntamente com algumas técnicas para iterar e manipular dicionários.
setdefault() para inserir sem errosO conceito de dicionário já tinha mostrado que .get(key, <default value>) devolve um value existente ou o default value se uma key não for encontrada num dicionário, evitando assim um KeyError.
Isto funciona bem em situações em que preferes não ter tratamento de erros extra, mas não podes confiar que a key procurada esteja presente.
Para uma operação de inserção igualmente "segura" (sem KeyError), existe o método .setdefault(key, <default value>).
setdefault(key, <default value>) devolve o value se a key for encontrada no dicionário.
Se a chave não for encontrada, insere o par (key, default value) e devolve o default value para ser usado.
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
# Looking for the value associated with key "Rock Brown".
# The key does not exist, so it is added with the default value, and the value is returned.
>>> palette_I.setdefault('Rock Brown', '#694605')
'#694605'
# The (key, default value) pair has now been added to the dictionary.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
fromkeys() para preencher um dicionário a partir de um iterávelPara preencher rapidamente um dicionário com várias keys e valores predefinidos, o método de classe fromkeys(iterable, <default value>) itera sobre um iterável de keys e cria um novo dict.
Todos os values são definidos com o default value fornecido:
>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
{'Grassy Green': 'fill in hex color here',
'Purple Mountains Majesty': 'fill in hex color here',
'Misty Mountain Pink': 'fill in hex color here'}
Os métodos .keys(), .values() e .items() devolvem vistas iteráveis de um dicionário.
Estas vistas podem ser usadas para percorrer facilmente as entradas sem as alterar.
As vistas são também dinâmicas: quando os dados subjacentes do dicionário mudam, o view object associado reflete essa mudança:
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
# Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
# Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
# Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
# Views are dynamic. Changing values in the dict changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
>>> palette_I['Deep Red'] = '#932432'
>>> palette_I.values()
dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
.keys(), .values() e .items()
No Python 3.7+, os dicts preservam a ordem pela qual as entradas são inseridas, o que permite uma iteração First-in, First-out (FIFO) ao usar .keys(), .values() ou .items().
No Python 3.8+, as vistas são também reversíveis.
Isto permite iterar sobre chaves, valores ou pares (key, value) na ordem Last-in, First-out (LIFO) usando reversed(<dict>.keys()), reversed(<dict>.values()) ou reversed(<dict>.items()):
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
# Iterating in insertion order (First in, first out)
>>> for item in palette_II.items():
... print(item)
...
('Factory Stone Purple', '#7c677f')
('Green Treeline', '#478559')
('Purple baseline', '#161748')
# Iterating in the reverse direction. (Last in, first out)
>>> for item in reversed(palette_II.items()):
... print (item)
...
('Purple baseline', '#161748')
('Green Treeline', '#478559')
('Factory Stone Purple', '#7c677f')
Os dicionários não têm um método de ordenação incorporado.
No entanto, é possível ordenar uma vista de um dict usando a função incorporada sorted() com dict.items().
A vista ordenada pode depois ser usada para criar um novo dicionário.
Tal como na iteração, a ordenação predefinida é feita sobre as keys do dicionário.
# Default ordering for a dictionary is insertion order (First in, first out).
>>> color_palette = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
# The default sort order for a dictionary uses the keys.
>>> sorted_palette = dict(sorted(color_palette.items()))
>>> sorted_palette
{'Factory Stone Purple': '#7c677f',
'Grassy Green': '#9bc400',
'Green Treeline': '#478559',
'Misty Mountain Pink': '#f9c5bd',
'Purple Mountains Majesty': '#8076a3',
'Purple baseline': '#161748'}
.update()
<dict_one>.update(<dict_two>) pode ser usado para combinar dois dicionários.
Este método pega nos pares (key,value) de <dict_two> e escreve-os em <dict_one>:
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple Baseline': '#161748'}
>>> palette_I.update(palette_II)
# Note that new items from palette_II are added.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
Quando as chaves dos dois dicionários coincidem, o value em dict_one é substituído pelo value correspondente de dict_two:
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> palette_I.update(palette_III)
# Overlapping values in palette_I are replaced with values from palette_III
>>> palette_I
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559', 'Purple baseline': '#161748'}
| e |=)O Python 3.9 introduz uma forma diferente de unir dicts: os operadores union.
dict_one | dict_two cria um novo dicionário, composto pelos pares (key, value) de dict_one e dict_two.
Quando ambos os dicionários partilham chaves, os valores de dict_two têm precedência.
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> new_dict = palette_I | palette_II
>>> new_dict
...
{'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
dict_one |= other comporta-se de forma semelhante a <dict_one>.update(<other>), mas, neste caso, other pode ser um dict ou um iterável de pares (key, value):
>>> palette_III = {'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> new_dict |= palette_III
>>> new_dict
...
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
Para uma explicação detalhada dos dicionários e dos métodos para trabalhar com eles, o tutorial oficial e a referência oficial da biblioteca são excelentes pontos de partida.
O Real Python e o Finxter têm também artigos muito completos sobre dicionários em Python.
A Mecha Munch™, uma empresa de automatização de compras de supermercado, acabou de te contratar para trabalhares na aplicação de encomendas deles. A tua equipa tem a tarefa de construir um MVP (produto mínimo viável) que faça a gestão de todas as atividades básicas do carrinho de compras, permitindo aos utilizadores adicionar, remover e ordenar as suas encomendas de mercearia. Felizmente, há outra equipa a tratar de todas as funções de pagamento e de finalização da compra!
O MVP deve permitir que o utilizador adicione artigos ao seu carrinho de compras. Pode ser um único artigo ou vários artigos ao mesmo tempo. Como isto é um MVP, a quantidade de artigos é indicada por repetições. Se um utilizador quiser adicionar 2 Oranges, 'Oranges' vai aparecer duas vezes no iterável de entrada. Se o utilizador já tiver o artigo no carrinho, a quantidade no carrinho deve ser aumentada em 1. Se o artigo for novo no carrinho, deve ser adicionado com uma quantidade de 1.
Cria a função add_item(<current_cart>, <items_to_add>) que recebe um dicionário de carrinho e qualquer iterável do tipo lista de artigos a adicionar como argumentos.
Deve devolver um novo dicionário de carrinho de compras atualizado para o utilizador.
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
{'Banana': 4, 'Apple': 5, 'Orange': 2}
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
['Banana', 'Orange', 'Blueberries', 'Banana'])
{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}
Ora bolas. Parece que a equipa de produto está a deixar-se levar pelo aumento descontrolado de funcionalidades. Querem adicionar funcionalidades extra ao MVP. A aplicação passa agora a ter de criar um carrinho de compras lendo artigos a partir da aplicação de notas do utilizador. É cómodo para os utilizadores, mas dá um pouco mais de trabalho à equipa.
Cria a função read_notes(<notes>) que pode receber qualquer iterável do tipo lista como argumento.
A função deve analisar os artigos e criar um carrinho de compras/dicionário do utilizador.
Cada artigo deve ser adicionado com uma quantidade de 1.
O novo carrinho do utilizador deve depois ser devolvido.
>>> read_notes(('Banana','Apple', 'Orange'))
{'Banana': 1, 'Apple': 1, 'Orange': 1}
>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}
A aplicação tem uma secção de "ideias" preenchida com receitas prontas de várias cozinhas do mundo. O utilizador pode escolher qualquer uma destas receitas e fazer com que todos os seus ingredientes sejam adicionados automaticamente ao carrinho de compras. A gestora de projeto pediu-te para criares uma forma de editar estas receitas de "ideias", uma vez que a equipa de conteúdos está sempre a alterar ingredientes e quantidades.
Cria a função update_recipes(<ideas>, <recipe_updates>) que recebe um dicionário de "ideias" e um iterável de atualizações de receitas como argumentos.
A função deve devolver o dicionário de "ideias" novo/atualizado.
>>>update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
(('Banana Bread', {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),)
)
...
{'Banana Bread': {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
'Raspberry Pie': {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}
>>> update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
[('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})]
)
...
{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
Depois de o utilizador ter começado um carrinho, a aplicação permite-lhe ordenar os artigos por ordem alfabética. Isto torna as coisas mais fáceis de encontrar e ajuda quando há erros de introdução de dados, como ter 'potatoes' e 'Potato' na base de dados.
Cria a função sort_entries(<cart>) que recebe um carrinho de compras/dicionário como argumento e devolve um novo, ordenado alfabeticamente.
>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}
A aplicação tem de enviar o carrinho de um determinado utilizador para a loja para processamento. No entanto, os funcionários da loja precisam de saber em que corredor da loja se encontra o artigo e se este precisa de refrigeração. Por isso (de forma bastante arbitrária), o "carrinho de processamento" tem de ser ordenado por ordem alfabética inversa, com as quantidades dos artigos combinadas com a informação de localização e de refrigeração.
Cria a função send_to_store(<cart>, <aisle_mapping>) que recebe um carrinho de compras do utilizador e um dicionário que tem o número do corredor da loja e um True/False para a refrigeração necessária de cada artigo.
A função deve return um "carrinho de processamento" combinado que tenha (quantidade, corredor e refrigeração) para cada artigo que o cliente está a encomendar.
Os artigos devem aparecer por ordem alfabética inversa.
>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
{'Banana': ['Aisle 5', False], 'Apple': ['Aisle 4', False], 'Orange': ['Aisle 4', False], 'Milk': ['Aisle 2', True]})
{'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]}
A aplicação não pode limitar-se a fazer encomendas de clientes sem fim. Mais cedo ou mais tarde, a loja vai ficar sem diversos produtos. Por isso, o MVP da tua aplicação tem de atualizar o inventário da loja sempre que um utilizador envia a sua encomenda para a loja. Caso contrário, os clientes vão encomendar produtos que não estão realmente disponíveis.
Cria a função update_store_inventory(<fulfillment_cart>, <store_inventory>) que recebe um "carrinho de processamento" e um inventário da loja.
A função deve reduzir as quantidades do inventário da loja pelo número "encomendado" no "carrinho de processamento" e depois devolver o inventário da loja atualizado.
Quando a contagem de um artigo da loja chega a 0, a contagem deve ser substituída pela mensagem 'Out of Stock'.
>>> update_store_inventory({'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]},
{'Banana': [15, 'Aisle 5', False], 'Apple': [12, 'Aisle 4', False], 'Orange': [1, 'Aisle 4', False], 'Milk': [4, 'Aisle 2', True]})
{'Banana': [12, 'Aisle 5', False], 'Apple': [10, 'Aisle 4', False], 'Orange': ['Out of Stock', 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True]}
Inscreve-te no Exercism para aprenderes e dominares Python com 17 conceitos146 exercícios, e mentoria humana real, tudo grátis.