16. Dictionnaires : associer des clés à des valeurs
Dans les chapitres précédents, nous avons appris à connaître les listes et les tuples — des collections qui stockent des éléments dans un ordre spécifique et nous permettent d’y accéder par position. Mais que faire si nous voulons rechercher une information par quelque chose de plus significatif qu’un nombre ? Et si nous voulons trouver la note d’un élève à partir de son nom, ou le prix d’un produit à partir de son ID, ou la définition d’un mot à partir du mot lui-même ?
C’est là qu’interviennent les dictionnaires(dictionary). Un dictionnaire est la structure de données intégrée de Python pour stocker des paires clé-valeur(key-value pairs). Au lieu d’accéder aux éléments par leur position (comme grades[0]), nous y accédons par leur clé(key) (comme grades["Alice"]). Cela rend les dictionnaires incroyablement puissants pour organiser et récupérer des données dans des programmes réels.
Pensez à un dictionnaire comme à un dictionnaire du monde réel ou à un annuaire : vous cherchez un mot (la clé) pour trouver sa définition (la valeur), ou vous cherchez un nom pour trouver un numéro de téléphone. Les dictionnaires Python fonctionnent de la même manière : ils associent des clés à des valeurs, ce qui permet des recherches rapides et une organisation flexible des données.
16.1) Créer des dictionnaires et accéder aux valeurs
16.1.1) Qu’est-ce qu’un dictionnaire ?
Un dictionnaire(dictionary) est une collection de paires clé-valeur(key-value pairs). Chaque clé est associée à une valeur, et vous utilisez la clé pour récupérer la valeur. Les clés doivent être uniques au sein d’un dictionnaire — vous ne pouvez pas avoir deux entrées avec la même clé. Les valeurs, en revanche, peuvent être dupliquées.
Voici la structure de base :
- Clés : identifiants uniques utilisés pour rechercher des valeurs (comme des noms, des IDs, ou des libellés)
- Valeurs : les données associées à chaque clé (comme des notes, des prix, ou des descriptions)
Les dictionnaires sont :
- Mutables : vous pouvez ajouter, modifier ou supprimer des paires clé-valeur après création
- Non ordonnés (en Python 3.6 et antérieur) ou ordonnés par insertion (en Python 3.7+) : même si Python moderne préserve l’ordre dans lequel vous ajoutez les éléments, vous devriez considérer les dictionnaires comme des collections où l’on accède aux éléments par clé, pas par position
- Dynamiques : ils peuvent grandir et rétrécir selon les besoins
16.1.2) Créer des dictionnaires vides et simples
La manière la plus simple de créer un dictionnaire consiste à utiliser des accolades {} avec des paires clé-valeur séparées par des deux-points :
# Dictionnaire vide
empty_dict = {}
print(empty_dict) # Output: {}
print(type(empty_dict)) # Output: <class 'dict'>
# Dictionnaire avec des notes d'élèves
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
# Dictionnaire avec des prix de produits
prices = {"apple": 0.50, "banana": 0.30, "orange": 0.75}
print(prices) # Output: {'apple': 0.5, 'banana': 0.3, 'orange': 0.75}Remarquez la syntaxe : chaque paire clé-valeur s’écrit key: value, et les paires sont séparées par des virgules. Les clés ici sont des chaînes ("Alice", "apple"), et les valeurs sont des nombres, mais les clés et les valeurs peuvent être de nombreux types différents.
Vous pouvez aussi créer un dictionnaire en utilisant le constructeur dict() :
# Utiliser dict() avec des arguments nommés
student = dict(name="Alice", age=20, major="Computer Science")
print(student) # Output: {'name': 'Alice', 'age': 20, 'major': 'Computer Science'}
# Utiliser dict() avec une liste de tuples
colors = dict([("red", "#FF0000"), ("green", "#00FF00"), ("blue", "#0000FF")])
print(colors) # Output: {'red': '#FF0000', 'green': '#00FF00', 'blue': '#0000FF'}Le constructeur dict() est utile lorsque vous construisez des dictionnaires à partir d’autres structures de données ou lorsque vous souhaitez utiliser des identifiants Python comme clés (sans guillemets).
16.1.3) Accéder aux valeurs par clé
Pour récupérer une valeur depuis un dictionnaire, utilisez des crochets avec la clé :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Accéder à des valeurs individuelles
alice_grade = grades["Alice"]
print(alice_grade) # Output: 95
bob_grade = grades["Bob"]
print(bob_grade) # Output: 87C’est la manière la plus directe d’accéder aux valeurs d’un dictionnaire. Cependant, si vous essayez d’accéder à une clé qui n’existe pas, Python lève une KeyError :
grades = {"Alice": 95, "Bob": 87}
# WARNING: KeyError - for demonstration only
# print(grades["David"]) # PROBLEM: KeyError: 'David'Cette erreur se produit parce que "David" n’est pas une clé dans le dictionnaire. Nous apprendrons comment gérer cela en toute sécurité dans la sous-section suivante.
16.1.4) Accès sécurisé avec get()
Pour éviter une KeyError lorsqu’une clé peut ne pas exister, utilisez la méthode get(). Elle renvoie None (ou une valeur par défaut que vous spécifiez) si la clé n’est pas trouvée :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Accès sécurisé avec get()
alice_grade = grades.get("Alice")
print(alice_grade) # Output: 95
# La clé n'existe pas - renvoie None
david_grade = grades.get("David")
print(david_grade) # Output: None
# Fournir une valeur par défaut
david_grade = grades.get("David", 0)
print(david_grade) # Output: 0
# Utiliser get() dans une logique conditionnelle
if grades.get("Eve") is None:
print("Eve is not in the grade book") # Output: Eve is not in the grade bookLa méthode get() est plus sûre que l’accès direct par crochets lorsque vous n’êtes pas certain qu’une clé existe. Le second argument de get() est la valeur par défaut à renvoyer si la clé est manquante — si vous n’en fournissez pas, la valeur par défaut est None.
Voici un exemple pratique montrant quand get() est utile :
# Base de données d'étudiants avec des informations optionnelles
students = {
"Alice": {"age": 20, "major": "CS"},
"Bob": {"age": 19}, # Bob n'a pas encore déclaré de spécialité
"Charlie": {"major": "Math"} # L'âge de Charlie n'est pas enregistré
}
# Accéder en toute sécurité à des informations potentiellement manquantes
for name in ["Alice", "Bob", "Charlie"]:
student = students[name]
age = student.get("age", "Unknown")
major = student.get("major", "Undeclared")
print(f"{name}: Age {age}, Major {major}")
# Output:
# Alice: Age 20, Major CS
# Bob: Age 19, Major Undeclared
# Charlie: Age Unknown, Major Math16.1.5) Types de clés valides
Les clés d’un dictionnaire doivent être hachables(hashable) — un terme technique qui signifie que la valeur de la clé ne peut pas changer. En pratique, cela signifie :
Types de clés valides (immuables) :
- Chaînes :
"name","id_123" - Nombres :
42,3.14 - Tuples (s’ils ne contiennent que des éléments immuables) :
(1, 2),("x", "y") - Booléens :
True,False None
Types de clés invalides (mutables) :
- Listes :
[1, 2, 3]ne peut pas être une clé - Dictionnaires :
{"a": 1}ne peut pas être une clé - Ensembles :
{1, 2, 3}ne peut pas être une clé
# Clés valides
valid_dict = {
"name": "Alice", # Clé de type chaîne
42: "answer", # Clé de type entier
3.14: "pi", # Clé de type flottant
(1, 2): "coordinates", # Clé de type tuple
True: "yes", # Clé de type booléen
None: "nothing" # Clé None
}
print(valid_dict["name"]) # Output: Alice
print(valid_dict[42]) # Output: answer
print(valid_dict[(1, 2)]) # Output: coordinates
# WARNING: Invalid keys - for demonstration only
# invalid_dict = {[1, 2]: "list key"} # PROBLEM: TypeError: unhashable type: 'list'
# invalid_dict = {{1, 2}: "set key"} # PROBLEM: TypeError: unhashable type: 'set'Erreur courante des débutants : une erreur fréquente consiste à essayer d’utiliser une liste comme clé de dictionnaire parce que cela semble logique. Par exemple, vous pourriez vouloir utiliser des coordonnées [x, y] comme clé pour stocker des données de localisation. Quand Python lève TypeError: unhashable type: 'list', les débutants ne comprennent souvent pas pourquoi — après tout, la liste contient exactement les données qu’ils veulent utiliser comme clé.
La raison est que les listes sont mutables (elles peuvent changer), et Python a besoin que les clés de dictionnaire soient stables et inchangées. Si vous devez utiliser quelque chose de similaire à une liste comme clé, convertissez-la d’abord en tuple : tuple([1, 2]) devient (1, 2), ce qui peut être utilisé comme clé. Les tuples sont immuables, donc ils fonctionnent parfaitement :
# Wrong: trying to use a list as a key
# locations = {[0, 0]: "origin", [1, 0]: "east"} # PROBLEM: TypeError
# Right: convert to tuple
locations = {(0, 0): "origin", (1, 0): "east", (0, 1): "north"}
print(locations[(0, 0)]) # Output: origin
print(locations[(1, 0)]) # Output: eastLes valeurs, en revanche, peuvent être de n’importe quel type — mutable ou immutable :
# Les valeurs peuvent être de n'importe quel type
flexible_dict = {
"numbers": [1, 2, 3], # Valeur de type liste
"nested": {"a": 1, "b": 2}, # Valeur de type dictionnaire
"function": len, # Valeur de type fonction
"mixed": (1, [2, 3], {"x": 4}) # Tuple contenant des éléments mutables
}
print(flexible_dict["numbers"]) # Output: [1, 2, 3]
print(flexible_dict["nested"]["a"]) # Output: 1Nous explorerons le concept de hachabilité plus en profondeur au Chapitre 17, mais pour l’instant, souvenez-vous : utilisez des types immuables (chaînes, nombres, tuples) comme clés, et tout ira bien.
16.2) Ajouter et mettre à jour des entrées de dictionnaire
16.2.1) Ajouter de nouvelles paires clé-valeur
Ajouter une nouvelle entrée à un dictionnaire est simple : il suffit d’assigner une valeur à une nouvelle clé :
grades = {"Alice": 95, "Bob": 87}
print(grades) # Output: {'Alice': 95, 'Bob': 87}
# Ajouter un nouvel élève
grades["Charlie"] = 92
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
# Ajouter un autre élève
grades["Diana"] = 88
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92, 'Diana': 88}Si la clé n’existe pas, Python la crée. Si elle existe déjà, Python met à jour la valeur (ce que nous aborderons ensuite).
16.2.2) Mettre à jour des valeurs existantes
Pour mettre à jour une valeur, assignez simplement une nouvelle valeur à une clé existante :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
# Mettre à jour la note de Bob
grades["Bob"] = 90
print(grades) # Output: {'Alice': 95, 'Bob': 90, 'Charlie': 92}
# Mettre à jour plusieurs notes
grades["Alice"] = 98
grades["Charlie"] = 94
print(grades) # Output: {'Alice': 98, 'Bob': 90, 'Charlie': 94}Python ne fait pas de distinction entre ajout et mise à jour — la syntaxe est identique. Si la clé existe, la valeur est mise à jour ; sinon, une nouvelle entrée est créée.
Voici un exemple pratique montrant à la fois l’ajout et la mise à jour :
# Suivre l'inventaire
inventory = {"apple": 50, "banana": 30}
print("Initial inventory:", inventory) # Output: Initial inventory: {'apple': 50, 'banana': 30}
# Réapprovisionner les pommes (mise à jour d'une entrée existante)
inventory["apple"] = inventory["apple"] + 20
print("After restocking apples:", inventory) # Output: After restocking apples: {'apple': 70, 'banana': 30}
# Ajouter un nouveau produit (ajout d'une nouvelle clé)
inventory["orange"] = 40
print("After adding oranges:", inventory) # Output: After adding oranges: {'apple': 70, 'banana': 30, 'orange': 40}
# Vendre des bananes (mise à jour d'une entrée existante)
inventory["banana"] = inventory["banana"] - 10
print("After selling bananas:", inventory) # Output: After selling bananas: {'apple': 70, 'banana': 20, 'orange': 40}16.2.3) Utiliser update() pour fusionner des dictionnaires
La méthode update() ajoute plusieurs paires clé-valeur en une seule fois ou fusionne un autre dictionnaire dans le dictionnaire courant :
grades = {"Alice": 95, "Bob": 87}
print(grades) # Output: {'Alice': 95, 'Bob': 87}
# Ajouter plusieurs élèves en une seule fois
new_students = {"Charlie": 92, "Diana": 88}
grades.update(new_students)
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92, 'Diana': 88}
# Mettre à jour des clés existantes et en ajouter de nouvelles
more_updates = {"Bob": 90, "Eve": 85} # La note de Bob change, Eve est nouvelle
grades.update(more_updates)
print(grades) # Output: {'Alice': 95, 'Bob': 90, 'Charlie': 92, 'Diana': 88, 'Eve': 85}La méthode update() modifie le dictionnaire en place. Si une clé existe déjà, sa valeur est mise à jour ; sinon, la paire clé-valeur est ajoutée.
Vous pouvez aussi passer des arguments nommés à update() :
student = {"name": "Alice", "age": 20}
print(student) # Output: {'name': 'Alice', 'age': 20}
# Mettre à jour en utilisant des arguments nommés
student.update(age=21, major="Computer Science")
print(student) # Output: {'name': 'Alice', 'age': 21, 'major': 'Computer Science'}Voici un exemple pratique de fusion de paramètres de configuration :
# Paramètres par défaut
config = {
"theme": "light",
"font_size": 12,
"auto_save": True
}
# Préférences utilisateur (remplacent certains défauts)
user_prefs = {
"theme": "dark",
"font_size": 14
}
# Fusionner les préférences utilisateur dans config
config.update(user_prefs)
print(config) # Output: {'theme': 'dark', 'font_size': 14, 'auto_save': True}16.2.4) Utiliser setdefault() pour n’ajouter que si la clé n’existe pas
La méthode setdefault() est utile lorsque vous souhaitez ajouter une paire clé-valeur uniquement si la clé n’existe pas déjà. Si la clé existe, elle renvoie la valeur actuelle sans la modifier :
grades = {"Alice": 95, "Bob": 87}
# Ajouter Charlie (la clé n'existe pas)
result = grades.setdefault("Charlie", 90)
print(result) # Output: 90
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 90}
# Essayer d'ajouter Alice (la clé existe - aucun changement)
result = grades.setdefault("Alice", 80)
print(result) # Output: 95 (existing value returned)
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 90} (unchanged)C’est particulièrement utile lorsque vous souhaitez vous assurer que toutes les clés de configuration requises existent avec des valeurs par défaut, tout en préservant les valeurs que l’utilisateur a déjà personnalisées :
# Configuration de l'application avec des valeurs par défaut
config = {"theme": "light", "font_size": 12}
# S'assurer que tous les paramètres requis existent avec des valeurs par défaut
config.setdefault("auto_save", True)
config.setdefault("language", "en")
config.setdefault("theme", "dark") # Ne changera pas - existe déjà
print(config)
# Output: {'theme': 'light', 'font_size': 12, 'auto_save': True, 'language': 'en'}
# Accéder maintenant en toute sécurité à tous les paramètres
print(f"Theme: {config['theme']}") # Output: Theme: light
print(f"Auto-save: {config['auto_save']}") # Output: Auto-save: True
print(f"Language: {config['language']}") # Output: Language: en16.3) Supprimer des entrées de dictionnaire avec del et pop()
16.3.1) Supprimer des entrées avec del
L’instruction del supprime une paire clé-valeur d’un dictionnaire :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92, 'Diana': 88}
# Supprimer Charlie
del grades["Charlie"]
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Diana': 88}
# Supprimer un autre élève
del grades["Bob"]
print(grades) # Output: {'Alice': 95, 'Diana': 88}Si vous essayez de supprimer une clé qui n’existe pas, Python lève une KeyError :
grades = {"Alice": 95, "Bob": 87}
# WARNING: KeyError - for demonstration only
# del grades["Charlie"] # PROBLEM: KeyError: 'Charlie'Pour supprimer en toute sécurité une clé qui peut ne pas exister, vérifiez d’abord :
grades = {"Alice": 95, "Bob": 87}
# Suppression sécurisée
if "Charlie" in grades:
del grades["Charlie"]
else:
print("Charlie not found") # Output: Charlie not found16.3.2) Supprimer et récupérer avec pop()
La méthode pop() supprime une clé et renvoie sa valeur. C’est utile lorsque vous devez à la fois supprimer une entrée et utiliser sa valeur :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
# Supprimer et récupérer la note de Bob
bob_grade = grades.pop("Bob")
print(bob_grade) # Output: 87
print(grades) # Output: {'Alice': 95, 'Charlie': 92}
# Supprimer et utiliser la valeur
charlie_grade = grades.pop("Charlie")
print(f"Charlie's final grade was {charlie_grade}") # Output: Charlie's final grade was 92
print(grades) # Output: {'Alice': 95}Comme del, pop() lève une KeyError si la clé n’existe pas. Cependant, vous pouvez fournir une valeur par défaut à renvoyer à la place :
grades = {"Alice": 95, "Bob": 87}
# Pop avec une valeur par défaut (la clé n'existe pas)
diana_grade = grades.pop("Diana", 0)
print(diana_grade) # Output: 0
print(grades) # Output: {'Alice': 95, 'Bob': 87} (unchanged)
# Pop avec une valeur par défaut (la clé existe)
alice_grade = grades.pop("Alice", 0)
print(alice_grade) # Output: 95
print(grades) # Output: {'Bob': 87}16.3.3) Supprimer toutes les entrées avec clear()
La méthode clear() supprime toutes les paires clé-valeur d’un dictionnaire, le laissant vide :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(grades) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
# Vider toutes les entrées
grades.clear()
print(grades) # Output: {}
print(len(grades)) # Output: 0C’est plus explicite que de réassigner à un dictionnaire vide (grades = {}), en particulier lorsque d’autres variables référencent le même dictionnaire :
# Démontrer la différence
grades = {"Alice": 95, "Bob": 87}
backup = grades # backup référence le même dictionnaire
# Utiliser clear() - affecte les deux variables
grades.clear()
print(grades) # Output: {}
print(backup) # Output: {} (same dictionary was cleared)
# Réinitialiser pour l'exemple suivant
grades = {"Alice": 95, "Bob": 87}
backup = grades
# Réassignation - n'affecte que grades
grades = {}
print(grades) # Output: {}
print(backup) # Output: {'Alice': 95, 'Bob': 87} (different dictionary now)Nous explorerons ce comportement plus en détail au Chapitre 18 lorsque nous discuterons de la sémantique des références, mais pour l’instant, retenez ceci : clear() vide le dictionnaire existant, tandis que la réassignation crée un nouveau dictionnaire vide.
16.4) Objets de vue de dictionnaire : keys(), values() et items()
16.4.1) Comprendre les vues de dictionnaire
Les dictionnaires fournissent trois méthodes qui renvoient des objets de vue(view objects) — des objets spéciaux qui fournissent une vue dynamique des clés, des valeurs, ou des paires clé-valeur du dictionnaire. Ces vues reflètent automatiquement les changements du dictionnaire :
keys(): renvoie une vue de toutes les clésvalues(): renvoie une vue de toutes les valeursitems(): renvoie une vue de toutes les paires clé-valeur (sous forme de tuples)
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Obtenir des vues
keys_view = grades.keys()
values_view = grades.values()
items_view = grades.items()
print(keys_view) # Output: dict_keys(['Alice', 'Bob', 'Charlie'])
print(values_view) # Output: dict_values([95, 87, 92])
print(items_view) # Output: dict_items([('Alice', 95), ('Bob', 87), ('Charlie', 92)])Ce ne sont pas des listes — ce sont des objets de vue. Mais vous pouvez les convertir en listes si nécessaire :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Convertir les vues en listes
keys_list = list(grades.keys())
values_list = list(grades.values())
items_list = list(grades.items())
print(keys_list) # Output: ['Alice', 'Bob', 'Charlie']
print(values_list) # Output: [95, 87, 92]
print(items_list) # Output: [('Alice', 95), ('Bob', 87), ('Charlie', 92)]16.4.2) Les vues sont dynamiques
Les objets de vue reflètent automatiquement les changements du dictionnaire :
grades = {"Alice": 95, "Bob": 87}
# Créer une vue
keys_view = grades.keys()
print(keys_view) # Output: dict_keys(['Alice', 'Bob'])
# Modifier le dictionnaire
grades["Charlie"] = 92
print(keys_view) # Output: dict_keys(['Alice', 'Bob', 'Charlie'])
# Supprimer une entrée
del grades["Bob"]
print(keys_view) # Output: dict_keys(['Alice', 'Charlie'])Ce comportement dynamique est utile lorsque vous devez travailler avec le contenu d’un dictionnaire susceptible de changer. Cependant, si vous avez besoin d’un instantané qui ne changera pas, convertissez la vue en liste :
grades = {"Alice": 95, "Bob": 87}
# Créer un instantané
keys_snapshot = list(grades.keys())
print(keys_snapshot) # Output: ['Alice', 'Bob']
# Modifier le dictionnaire
grades["Charlie"] = 92
print(keys_snapshot) # Output: ['Alice', 'Bob'] (unchanged)16.4.3) Travailler avec keys()
La méthode keys() renvoie une vue de toutes les clés du dictionnaire. C’est utile pour vérifier quelles clés existent ou pour itérer dessus :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Obtenir toutes les clés
keys = grades.keys()
print(keys) # Output: dict_keys(['Alice', 'Bob', 'Charlie'])
# Vérifier si une clé existe
if "Alice" in keys:
print("Alice is in the grade book") # Output: Alice is in the grade book
# Compter les clés
print(f"Number of students: {len(keys)}") # Output: Number of students: 3Vous pouvez aussi vérifier l’appartenance directement sur le dictionnaire sans appeler keys() :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Ces deux approches sont équivalentes
if "Alice" in grades.keys():
print("Found (using keys())")
if "Alice" in grades:
print("Found (direct check)") # Ceci est plus courant et plus concis
# Output:
# Found (using keys())
# Found (direct check)16.4.4) Travailler avec values()
La méthode values() renvoie une vue de toutes les valeurs du dictionnaire. C’est utile lorsque vous devez traiter les valeurs sans vous soucier de leurs clés :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
# Obtenir toutes les valeurs
values = grades.values()
print(values) # Output: dict_values([95, 87, 92, 88])
# Calculer des statistiques
total = sum(values)
count = len(values)
average = total / count
print(f"Total points: {total}") # Output: Total points: 362
print(f"Number of students: {count}") # Output: Number of students: 4
print(f"Average grade: {average}") # Output: Average grade: 90.5
# Trouver la note la plus haute et la plus basse
print(f"Highest grade: {max(values)}") # Output: Highest grade: 95
print(f"Lowest grade: {min(values)}") # Output: Lowest grade: 8716.4.5) Travailler avec items()
La méthode items() renvoie une vue des paires clé-valeur sous forme de tuples. C’est la vue la plus couramment utilisée parce qu’elle vous donne à la fois les clés et les valeurs :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Obtenir toutes les paires clé-valeur
items = grades.items()
print(items) # Output: dict_items([('Alice', 95), ('Bob', 87), ('Charlie', 92)])
# Convertir en liste pour voir clairement les tuples
items_list = list(items)
print(items_list) # Output: [('Alice', 95), ('Bob', 87), ('Charlie', 92)]
# Accéder à des tuples individuels
first_item = items_list[0]
print(first_item) # Output: ('Alice', 95)
print(first_item[0]) # Output: Alice
print(first_item[1]) # Output: 95La vue items() est particulièrement utile pour l’itération, que nous aborderons en détail dans la section suivante. Voici un aperçu :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Traiter chaque paire clé-valeur
for name, grade in grades.items():
print(f"{name}: {grade}")
# Output:
# Alice: 95
# Bob: 87
# Charlie: 9216.5) Itérer sur les clés, les valeurs et les éléments
16.5.1) Itérer sur les clés (comportement par défaut)
Lorsque vous itérez directement sur un dictionnaire avec une boucle for, vous itérez sur ses clés :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Itérer sur les clés (implicite)
for name in grades:
print(name)
# Output:
# Alice
# Bob
# CharlieC’est équivalent à itérer sur grades.keys() :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Itérer sur les clés (explicite)
for name in grades.keys():
print(name)
# Output:
# Alice
# Bob
# CharlieLes deux approches fonctionnent de manière identique. La version implicite (sans .keys()) est plus courante et plus concise.
Voici un exemple pratique utilisant les clés pour accéder aux valeurs :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
# Trouver les élèves qui ont réussi (note >= 90)
passing_students = []
for name in grades:
if grades[name] >= 90:
passing_students.append(name)
print("Students who passed:", passing_students) # Output: Students who passed: ['Alice', 'Charlie']16.5.2) Itérer sur les valeurs
Pour itérer uniquement sur les valeurs, utilisez values() :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
# Itérer sur les valeurs
for grade in grades.values():
print(grade)
# Output:
# 95
# 87
# 92
# 88C’est utile lorsque vous devez traiter les valeurs mais que vous ne vous souciez pas de la clé à laquelle elles sont associées :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
# Calculer le total et la moyenne
total = 0
count = 0
for grade in grades.values():
total = total + grade
count = count + 1
average = total / count
print(f"Class average: {average}") # Output: Class average: 90.5
# Vérifier si tous les élèves ont réussi
all_passed = True
for grade in grades.values():
if grade < 60:
all_passed = False
break
if all_passed:
print("All students passed!") # Output: All students passed!16.5.3) Itérer sur les paires clé-valeur avec items()
Le schéma d’itération le plus courant et le plus utile consiste à utiliser items() pour obtenir à la fois les clés et les valeurs :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Itérer sur les paires clé-valeur
for name, grade in grades.items():
print(f"{name} scored {grade}")
# Output:
# Alice scored 95
# Bob scored 87
# Charlie scored 92Remarquez le déballage du tuple : for name, grade in grades.items(). Chaque élément est un tuple comme ("Alice", 95), et nous le déballons en deux variables. C’est bien plus lisible que d’accéder aux indices du tuple :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Sans déballage (moins lisible)
for item in grades.items():
print(f"{item[0]} scored {item[1]}")
# Avec déballage (plus lisible)
for name, grade in grades.items():
print(f"{name} scored {grade}")
# Both produce the same output:
# Alice scored 95
# Bob scored 87
# Charlie scored 9216.5.4) Modifier des dictionnaires pendant l’itération
Avertissement : modifier la taille d’un dictionnaire (ajouter ou supprimer des clés) pendant qu’on itère dessus peut provoquer des erreurs ou un comportement inattendu :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# WARNING: RuntimeError - for demonstration only
# for name in grades:
# if grades[name] < 90:
# del grades[name] # PROBLEM: RuntimeError: dictionary changed size during iterationDans Python moderne (3.7+), cela lève immédiatement une RuntimeError dès que vous essayez de changer la taille du dictionnaire. Python détecte la modification et arrête l’exécution afin d’éviter un comportement imprévisible.
Dans les anciennes versions de Python, cela pouvait amener l’itérateur à :
- Sauter des éléments qu’il devrait traiter
- Traiter deux fois le même élément
- Produire des résultats incohérents
C’est pourquoi Python échoue désormais rapidement avec un message d’erreur clair.
Si vous devez modifier un dictionnaire pendant l’itération, itérez sur une copie des clés :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
# Sécurisé : itérer sur une copie des clés
for name in list(grades.keys()):
if grades[name] < 90:
del grades[name]
print(grades) # Output: {'Alice': 95, 'Charlie': 92}Ou construisez un nouveau dictionnaire contenant uniquement les entrées que vous souhaitez :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
# Construire un nouveau dictionnaire avec des entrées filtrées
high_grades = {}
for name, grade in grades.items():
if grade >= 90:
high_grades[name] = grade
print(high_grades) # Output: {'Alice': 95, 'Charlie': 92}La seconde approche est souvent plus claire et plus sûre. Nous apprendrons des manières encore plus élégantes de faire cela avec les compréhensions de dictionnaire au Chapitre 35.
16.6) Méthodes courantes des dictionnaires
16.6.1) Vérifier la présence de clés avec in et not in
Les opérateurs in et not in vérifient si une clé existe dans un dictionnaire :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
# Vérifier si des clés existent
if "Alice" in grades:
print("Alice is in the grade book") # Output: Alice is in the grade book
if "David" not in grades:
print("David is not in the grade book") # Output: David is not in the grade bookC’est la méthode préférée pour vérifier l’existence d’une clé avant d’accéder aux valeurs. C’est plus lisible et plus Pythonique que d’utiliser get() et de tester None :
grades = {"Alice": 95, "Bob": 87}
# Préféré : utiliser in
if "Alice" in grades:
print(f"Alice's grade: {grades['Alice']}") # Output: Alice's grade: 95
# Alternative : utiliser get() et tester None
if grades.get("Alice") is not None:
print(f"Alice's grade: {grades['Alice']}") # Output: Alice's grade: 9516.6.2) Obtenir le nombre d’entrées avec len()
La fonction len() renvoie le nombre de paires clé-valeur dans un dictionnaire :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92}
print(len(grades)) # Output: 3
# Dictionnaire vide
empty = {}
print(len(empty)) # Output: 0
# Après modifications
grades["Diana"] = 88
print(len(grades)) # Output: 4
del grades["Bob"]
print(len(grades)) # Output: 3C’est utile pour vérifier si un dictionnaire est vide ou pour afficher des statistiques :
grades = {"Alice": 95, "Bob": 87, "Charlie": 92, "Diana": 88}
if len(grades) == 0:
print("No students in grade book")
else:
total = sum(grades.values())
average = total / len(grades)
print(f"{len(grades)} students, average grade: {average}")
# Output: 4 students, average grade: 90.516.6.3) Copier des dictionnaires avec copy()
La méthode copy() crée une copie superficielle(shallow copy) d’un dictionnaire — un nouveau dictionnaire avec les mêmes paires clé-valeur :
original = {"Alice": 95, "Bob": 87}
duplicate = original.copy()
print(original) # Output: {'Alice': 95, 'Bob': 87}
print(duplicate) # Output: {'Alice': 95, 'Bob': 87}
# Modifier la copie
duplicate["Charlie"] = 92
print(original) # Output: {'Alice': 95, 'Bob': 87} (unchanged)
print(duplicate) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}C’est différent d’une simple assignation, qui crée une référence vers le même dictionnaire :
original = {"Alice": 95, "Bob": 87}
reference = original # Pas une copie - même dictionnaire
# Modifier via la référence
reference["Charlie"] = 92
print(original) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92} (changed!)
print(reference) # Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}Nous explorerons en détail les copies superficielles versus profondes au Chapitre 18. Pour l’instant, retenez ceci : utilisez copy() lorsque vous voulez un double indépendant d’un dictionnaire.
16.6.4) Fusionner des dictionnaires avec l’opérateur | (Python 3.9+)
Python 3.9 a introduit l’opérateur | pour fusionner des dictionnaires. L’opérateur | crée un nouveau dictionnaire combinant toutes les clés des deux dictionnaires. En cas de clé en double, la valeur de droite remplace la valeur de gauche. Les deux dictionnaires d’origine restent inchangés.
defaults = {"theme": "light", "font_size": 12, "auto_save": True}
user_prefs = {"theme": "dark", "font_size": 14}
# Fusionner des dictionnaires (user_prefs remplace defaults)
config = defaults | user_prefs
print(config) # Output: {'theme': 'dark', 'font_size': 14, 'auto_save': True}
# Dictionnaires d'origine inchangés
print(defaults) # Output: {'theme': 'light', 'font_size': 12, 'auto_save': True}
print(user_prefs) # Output: {'theme': 'dark', 'font_size': 14}Voici un autre exemple montrant comment c’est utile pour combiner des données provenant de plusieurs sources :
# Informations produit provenant de deux fournisseurs
supplier_a = {
"laptop": {"price": 999.99, "stock": 15},
"mouse": {"price": 29.99, "stock": 50}
}
supplier_b = {
"laptop": {"price": 949.99, "stock": 20}, # Meilleur prix et stock
"keyboard": {"price": 79.99, "stock": 30}
}
# Fusion : les données de supplier_b remplacent celles de supplier_a pour les produits correspondants
combined = supplier_a | supplier_b
print(combined)
# Output: {'laptop': {'price': 949.99, 'stock': 20}, 'mouse': {'price': 29.99, 'stock': 50}, 'keyboard': {'price': 79.99, 'stock': 30}}
# Maintenant nous avons laptop de supplier_b (meilleure offre), mouse de supplier_a, et keyboard de supplier_bL’opérateur |= fusionne en place (modifie le dictionnaire de gauche) :
config = {"theme": "light", "font_size": 12, "auto_save": True}
user_prefs = {"theme": "dark", "font_size": 14}
# Fusionner en place
config |= user_prefs
print(config) # Output: {'theme': 'dark', 'font_size': 14, 'auto_save': True}C’est équivalent à utiliser update() mais plus concis :
config = {"theme": "light", "font_size": 12}
user_prefs = {"theme": "dark", "font_size": 14}
# Ces deux approches sont équivalentes
config.update(user_prefs)
# config |= user_prefs
print(config) # Output: {'theme': 'dark', 'font_size': 14}16.7) Dictionnaires imbriqués pour des données structurées
16.7.1) Créer des dictionnaires imbriqués
Pourquoi utiliser des dictionnaires imbriqués ? Imaginez que vous suiviez les informations d’élèves. Vous pourriez créer des dictionnaires séparés : ages = {"Alice": 20, "Bob": 19}, majors = {"Alice": "CS", "Bob": "Math"}, gpas = {"Alice": 3.8, "Bob": 3.6}. Mais cela devient ingérable — vous devez garder trois dictionnaires synchronisés, et rechercher toutes les informations pour un élève nécessite trois recherches séparées. Les dictionnaires imbriqués résolvent ce problème en regroupant les données liées : un nom d’élève correspond à toutes ses informations en une seule recherche.
Un dictionnaire imbriqué(nested dictionary) est un dictionnaire qui contient d’autres dictionnaires comme valeurs. C’est utile pour représenter des données structurées et hiérarchiques :
# Dossiers d'élèves avec plusieurs attributs
students = {
"Alice": {
"age": 20,
"major": "Computer Science",
"gpa": 3.8
},
"Bob": {
"age": 19,
"major": "Mathematics",
"gpa": 3.6
},
"Charlie": {
"age": 21,
"major": "Physics",
"gpa": 3.9
}
}
print(students)
# Output: {'Alice': {'age': 20, 'major': 'Computer Science', 'gpa': 3.8}, 'Bob': {'age': 19, 'major': 'Mathematics', 'gpa': 3.6}, 'Charlie': {'age': 21, 'major': 'Physics', 'gpa': 3.9}}Chaque nom d’élève correspond à un autre dictionnaire contenant ses attributs. Cette structure est beaucoup plus flexible et plus maintenable que d’utiliser des dictionnaires séparés pour chaque attribut.
16.7.2) Accéder aux valeurs imbriquées
Pour accéder aux valeurs imbriquées, utilisez plusieurs paires de crochets :
students = {
"Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
"Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6}
}
# Accéder à des valeurs imbriquées
alice_age = students["Alice"]["age"]
print(alice_age) # Output: 20
bob_major = students["Bob"]["major"]
print(bob_major) # Output: Mathematics
# Utiliser dans des expressions
print(f"Alice is {students['Alice']['age']} years old")
# Output: Alice is 20 years old
print(f"Bob's GPA: {students['Bob']['gpa']}")
# Output: Bob's GPA: 3.6Chaque paire de crochets accède à un niveau d’imbrication. D’abord students["Alice"] récupère le dictionnaire interne, puis ["age"] récupère la valeur d’âge depuis ce dictionnaire.
16.7.3) Accéder en toute sécurité aux valeurs imbriquées
Lorsqu’on accède à des dictionnaires imbriqués, vous devez vérifier que chaque niveau existe :
students = {
"Alice": {"age": 20, "major": "Computer Science"},
"Bob": {"age": 19} # Bob n'a pas déclaré de spécialité
}
# Unsafe access - might raise KeyError
# print(students["Bob"]["major"]) # PROBLEM: KeyError: 'major'
# Accès sécurisé avec plusieurs vérifications
if "Bob" in students:
if "major" in students["Bob"]:
print(f"Bob's major: {students['Bob']['major']}")
else:
print("Bob hasn't declared a major") # Output: Bob hasn't declared a major
# Utiliser get() pour un accès imbriqué sécurisé
bob_major = students.get("Bob", {}).get("major", "Undeclared")
print(f"Bob's major: {bob_major}") # Output: Bob's major: UndeclaredLa chaîne de get() fonctionne parce que si "Bob" n’existe pas, get() renvoie un dictionnaire vide {}, puis le second get() renvoie en toute sécurité "Undeclared".
16.7.4) Modifier des dictionnaires imbriqués
Vous pouvez ajouter, mettre à jour ou supprimer des entrées dans des dictionnaires imbriqués :
students = {
"Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
"Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6}
}
# Mettre à jour une valeur imbriquée
students["Alice"]["gpa"] = 3.9
print(f"Alice's new GPA: {students['Alice']['gpa']}") # Output: Alice's new GPA: 3.9
# Ajouter un nouvel attribut à un élève existant
students["Bob"]["email"] = "bob@university.edu"
print(students["Bob"])
# Output: {'age': 19, 'major': 'Mathematics', 'gpa': 3.6, 'email': 'bob@university.edu'}
# Ajouter un nouvel élève avec des données imbriquées
students["Charlie"] = {
"age": 21,
"major": "Physics",
"gpa": 3.7
}
print(f"Number of students: {len(students)}") # Output: Number of students: 3
# Supprimer un attribut
del students["Bob"]["email"]
print(students["Bob"])
# Output: {'age': 19, 'major': 'Mathematics', 'gpa': 3.6}16.7.5) Itérer sur des dictionnaires imbriqués
Vous pouvez itérer sur des dictionnaires imbriqués en utilisant des boucles imbriquées :
students = {
"Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
"Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6},
"Charlie": {"age": 21, "major": "Physics", "gpa": 3.9}
}
# Itérer sur les élèves et leurs attributs
for name, info in students.items():
print(f"\n{name}:")
for key, value in info.items():
print(f" {key}: {value}")
# Output:
# Alice:
# age: 20
# major: Computer Science
# gpa: 3.8
#
# Bob:
# age: 19
# major: Mathematics
# gpa: 3.6
#
# Charlie:
# age: 21
# major: Physics
# gpa: 3.9Voici un exemple pratique pour trouver des élèves qui répondent à certains critères :
students = {
"Alice": {"age": 20, "major": "Computer Science", "gpa": 3.8},
"Bob": {"age": 19, "major": "Mathematics", "gpa": 3.6},
"Charlie": {"age": 21, "major": "Physics", "gpa": 3.9},
"Diana": {"age": 20, "major": "Computer Science", "gpa": 3.5}
}
# Trouver les étudiants en CS avec un GPA supérieur à 3.7
print("High-performing CS students:")
for name, info in students.items():
if info["major"] == "Computer Science" and info["gpa"] >= 3.7:
print(f" {name} (GPA: {info['gpa']})")
# Output:
# High-performing CS students:
# Alice (GPA: 3.8)Les dictionnaires sont l’une des structures de données les plus puissantes et polyvalentes de Python. Ils offrent des recherches rapides, une organisation flexible et des solutions élégantes à des problèmes de programmation courants. Au fur et à mesure que vous continuez à apprendre Python, vous verrez des dictionnaires apparaître partout — des paramètres de configuration au traitement de données, jusqu’à la construction d’applications complexes.
Les schémas que nous avons couverts dans ce chapitre — le comptage, le regroupement, les tables de correspondance et la transformation de données — constituent la base du travail avec des données structurées en Python. Dans le chapitre suivant, nous explorerons les ensembles, un autre type de collection qui complète les dictionnaires pour travailler avec des données uniques et non ordonnées.