La classe dict de Python fournit de nombreuses méthodes pour manipuler les dictionnaires.
Certaines ont été présentées dans le concept consacré aux dicts.
Nous en abordons ici quelques autres, ainsi que quelques techniques pour parcourir et manipuler les dictionnaires.
setdefault() pour insérer sans erreurLe concept sur les dictionnaires a déjà montré que .get(key, <default value>) renvoie une value existante ou la default value si une key est introuvable dans un dictionnaire, ce qui évite une KeyError.
C'est pratique quand tu préfères éviter une gestion d'erreurs supplémentaire, mais que tu ne peux pas garantir qu'une key recherchée sera présente.
Pour une opération d'insertion tout aussi « sûre » (sans KeyError), il existe la méthode .setdefault(key, <default value>).
setdefault(key, <default value>) renvoie la value si la key est présente dans le dictionnaire.
Si la clé n'est pas trouvée, elle insère le couple (key, default value) et renvoie la default value que tu pourras utiliser.
>>> 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() pour remplir un dictionnaire à partir d'un itérablePour remplir rapidement un dictionnaire avec différentes keys et des valeurs par défaut, la méthode de classe fromkeys(iterable, <default value>) parcourt un itérable de keys et crée un nouveau dict.
Toutes les values seront définies sur la default value fournie :
>>> 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'}
Les méthodes .keys(), .values() et .items() renvoient des vues itérables d'un dictionnaire.
Ces vues permettent de parcourir facilement les entrées sans les modifier.
Les vues sont aussi dynamiques. Quand les données sous-jacentes du dictionnaire changent, l'objet view object associé reflète ce changement :
>>> 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() et .items()
À partir de Python 3.7, les dicts conservent l'ordre d'insertion des entrées, ce qui permet une itération premier entré, premier sorti (FIFO) avec .keys(), .values() ou .items().
À partir de Python 3.8, les vues sont aussi réversibles.
Cela permet de parcourir les clés, les valeurs ou les couples (key, value) dans l'ordre dernier entré, premier sorti (LIFO) grâce à 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')
Les dictionnaires n'ont pas de méthode de tri intégrée.
Cependant, il est possible de trier une vue de dict avec la fonction intégrée sorted() et dict.items().
La vue triée peut ensuite servir à créer un nouveau dictionnaire.
Comme pour l'itération, le tri par défaut porte sur les keys du dictionnaire.
# 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>) permet de combiner deux dictionnaires.
Cette méthode prend les couples (key,value) de <dict_two> et les écrit dans <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'}
Quand les clés des deux dictionnaires se recouvrent, la value de dict_one est écrasée par la value correspondante 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'}
| et |=)Python 3.9 introduit un autre moyen de fusionner des dicts : les opérateurs d'union.
dict_one | dict_two crée un nouveau dictionnaire, composé des couples (key, value) de dict_one et dict_two.
Quand les deux dictionnaires partagent des clés, les valeurs de dict_two sont prioritaires.
>>> 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 se comporte comme <dict_one>.update(<other>), mais ici, other peut être soit un dict, soit un itérable de couples (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'}
Pour une explication détaillée des dictionnaires et des méthodes associées, le tutoriel officiel et la référence officielle de la bibliothèque sont d'excellents points de départ.
Real Python et Finxter proposent aussi des articles très complets sur les dictionnaires Python.
Mecha Munch™, une entreprise d'automatisation des courses, vient de t'embaucher pour travailler sur son application de commande. Ton équipe est chargée de construire un MVP (produit minimum viable) qui gère toutes les activités de base d'un panier d'achat, en permettant aux utilisateurs d'ajouter, de supprimer et de trier leurs commandes de courses. Heureusement, une autre équipe s'occupe de tout ce qui concerne l'argent et le passage en caisse !
Le MVP doit permettre à l'utilisateur d'ajouter des articles à son panier. Il peut s'agir d'un seul article ou de plusieurs à la fois. Comme il s'agit d'un MVP, la quantité d'un article est indiquée par des répétitions. Si un utilisateur veut ajouter 2 Oranges, « Oranges » apparaîtra deux fois dans l'itérable d'entrée. Si l'utilisateur a déjà l'article dans son panier, la quantité du panier doit être augmentée de 1. Si l'article est nouveau dans le panier, il doit y être ajouté avec une quantité de 1.
Crée la fonction add_item(<current_cart>, <items_to_add>) qui prend un dictionnaire représentant le panier et un itérable de type liste d'articles à ajouter en arguments.
Elle doit renvoyer un dictionnaire de panier d'achat nouveau ou mis à jour pour l'utilisateur.
>>> 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}
Oups. On dirait que l'équipe produit se lance dans la dérive des fonctionnalités. Elle veut ajouter des fonctionnalités supplémentaires au MVP. L'application doit désormais créer un panier d'achat en lisant les articles depuis l'application de notes d'un utilisateur. Pratique pour les utilisateurs, mais un peu plus de travail pour l'équipe.
Crée la fonction read_notes(<notes>) qui peut prendre n'importe quel itérable de type liste en argument.
La fonction doit analyser les articles et créer un panier d'achat (dictionnaire) pour l'utilisateur.
Chaque article doit être ajouté avec une quantité de 1.
Le nouveau panier de l'utilisateur doit ensuite être renvoyé.
>>> 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}
L'application possède une section « idées » remplie de recettes toutes prêtes issues de diverses cuisines. L'utilisateur peut sélectionner l'une de ces recettes et faire ajouter automatiquement tous ses ingrédients à son panier. Le chef de projet t'a demandé de créer un moyen de modifier ces recettes « idées », car l'équipe de contenu ne cesse de changer les ingrédients et les quantités.
Crée la fonction update_recipes(<ideas>, <recipe_updates>) qui prend un dictionnaire « idées » et un itérable de mises à jour de recettes en arguments.
La fonction doit renvoyer le dictionnaire « idées » nouveau ou mis à jour.
>>>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}}
Une fois que l'utilisateur a commencé un panier, l'application lui permet de trier ses articles par ordre alphabétique. Cela facilite la recherche des articles et aide en cas d'erreur de saisie, comme avoir « potatoes » et « Potato » dans la base de données.
Crée la fonction sort_entries(<cart>) qui prend un panier d'achat (dictionnaire) en argument et renvoie un nouveau panier trié par ordre alphabétique.
>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}
L'application doit envoyer le panier d'un utilisateur au magasin pour la préparation de sa commande. Cependant, les préparateurs du magasin doivent savoir dans quel rayon se trouve chaque article et si celui-ci doit être réfrigéré. Donc (de façon assez arbitraire), le « panier de préparation » doit être trié par ordre alphabétique inverse, avec les quantités d'articles combinées aux informations d'emplacement et de réfrigération.
Crée la fonction send_to_store(<cart>, <aisle_mapping>) qui prend un panier d'achat et un dictionnaire associant à chaque article un numéro de rayon et un True/False indiquant si la réfrigération est nécessaire.
La fonction doit return un « panier de préparation » combiné contenant (quantité, rayon et réfrigération) pour chaque article commandé par le client.
Les articles doivent apparaître par ordre alphabétique inverse.
>>> 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]}
L'application ne peut pas simplement enregistrer des commandes de clients à l'infini. Au bout d'un moment, le magasin va être à court de certains produits. Le MVP de ton application doit donc mettre à jour l'inventaire du magasin chaque fois qu'un utilisateur envoie sa commande. Sinon, les clients commanderont des produits qui ne sont en réalité pas disponibles.
Crée la fonction update_store_inventory(<fulfillment_cart>, <store_inventory>) qui prend un « panier de préparation » et un inventaire de magasin.
La fonction doit réduire les quantités de l'inventaire du magasin du nombre « commandé » dans le « panier de préparation », puis renvoyer l'inventaire mis à jour.
Lorsque le stock d'un article tombe à 0, la quantité doit être remplacée par le message « 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]}
Inscris-toi sur Exercism pour apprendre et maîtriser Python avec 17 concepts146 exercices, et un vrai mentorat humain, le tout gratuitement.