backend / python
Dictionnaires
Explication
Ce que vous allez apprendre
- Accéder, ajouter et supprimer des entrées dans un dictionnaire sans provoquer de
KeyError - Comprendre pourquoi l'accès à une clé est quasi instantané, même sur un très gros dictionnaire
- Utiliser
.get(),.setdefault()etcollections.defaultdictselon le besoin exact - Fusionner deux dictionnaires proprement avec l'opérateur
|(Python 3.9+) - Utiliser
collections.Counterpour compter des occurrences sans boucle manuelle
Dans quel contexte ?
Un développeur reçoit la réponse JSON d'une API météo sous la forme {"ville": "Paris", "temperature": 18, "humidite": 62} et doit en extraire la température pour l'afficher. Écrire reponse["temperature"] fonctionne tant que la clé existe, mais si l'API change un jour de format et omet ce champ, le programme plante avec un KeyError. Cette leçon montre comment accéder à une clé de façon sûre avec reponse.get("temperature", "inconnue").
Chercher par nom plutôt que par position
Une liste oblige à se souvenir de la position d'une information (le troisième élément est l'âge). Un dictionnaire remplace cette position par un nom explicite : on écrit utilisateur["age"] plutôt que utilisateur[2]. C'est la structure de données centrale de tout programme Python un peu sérieux, du simple stockage de configuration à la représentation d'un objet JSON complet.
Comment un dictionnaire retrouve une valeur aussi vite
Un dictionnaire ne parcourt pas ses entrées une par une pour retrouver une clé : il calcule une empreinte numérique (un hash) à partir de la clé, ce qui lui indique directement où chercher. C'est ce mécanisme qui rend l'accès à une clé quasi instantané, même sur un dictionnaire contenant un million d'entrées, là où une recherche dans une liste équivalente ralentirait proportionnellement à sa taille.
Éviter les erreurs d'accès
Accéder à une clé absente avec dictionnaire["absente"] lève une exception. C'est voulu : Python préfère signaler explicitement une erreur plutôt que renvoyer silencieusement une valeur incorrecte. Mais dans bien des cas, on veut une valeur par défaut sans risque de plantage : .get() et .setdefault() répondent à ce besoin, tout comme collections.defaultdict qui initialise automatiquement une valeur pour toute nouvelle clé.
| Méthode | Comportement si la clé est absente |
|---|---|
d[cle] | Lève KeyError |
d.get(cle, defaut) | Renvoie defaut sans erreur |
d.setdefault(cle, valeur) | Crée la clé avec valeur puis la renvoie |
defaultdict(list)[cle] | Crée automatiquement une liste vide |
Bonne pratique
Préférez reponse.get("temperature") à reponse["temperature"] dès que la donnée vient d'une source externe (API, fichier de config, entrée utilisateur) sur laquelle vous n'avez pas un contrôle total.
Un détail d'histoire du langage
Depuis Python 3.7, l'ordre d'insertion des clés est garanti et fait partie du comportement officiel du langage, ce qui n'était pas le cas avant. Cela ouvre des usages pratiques, comme dédupliquer une liste tout en conservant l'ordre d'apparition des éléments.
Les dictionnaires et les sets partagent la même mécanique de hachage : ce que vous venez d'apprendre sur les sets s'applique directement ici.
Commandes & code
Dictionnaires
Structure cle-valeur, ordonnee par insertion (depuis Python 3.7), tres optimisee.
# Creation
utilisateur = {"nom": "Alice", "age": 30, "ville": "Paris"}
vide = {}
via_constructeur = dict(nom="Bob", age=25)
# Acces et mutation
print(utilisateur["nom"])
utilisateur["email"] = "alice@example.com" # ajout
utilisateur["age"] = 31 # modification
# Acces securise : get() evite KeyError
telephone = utilisateur.get("telephone", "inconnu") # valeur par defaut
# setdefault : recupere ou initialise en une seule operation
groupes = {}
groupes.setdefault("admins", []).append("alice")
groupes.setdefault("admins", []).append("bob")
print(groupes) # {'admins': ['alice', 'bob']}
# Suppression
del utilisateur["ville"]
age = utilisateur.pop("age") # retire et retourne la valeur
utilisateur.pop("inexistant", None) # pas d'erreur si absent
# Parcours
for cle in utilisateur:
print(cle)
for cle, valeur in utilisateur.items():
print(f"{cle} -> {valeur}")
for valeur in utilisateur.values():
print(valeur)
# Fusion de dictionnaires (Python 3.9+)
defauts = {"theme": "clair", "langue": "fr"}
preferences_user = {"theme": "sombre"}
config = defauts | preferences_user # {'theme': 'sombre', 'langue': 'fr'}
defauts |= preferences_user # fusion en place
# Avant 3.9 : unpacking pour fusionner
config_legacy = {**defauts, **preferences_user}
# update()
defauts.update(preferences_user)
# collections.defaultdict : valeur par defaut automatique
from collections import defaultdict
compteur = defaultdict(int)
for mot in "le chat mange le poisson le chat dort".split():
compteur[mot] += 1 # pas besoin de verifier l'existence
print(dict(compteur))
groupes_par_lettre = defaultdict(list)
for mot in ["chat", "chien", "cheval", "biche"]:
groupes_par_lettre[mot[0]].append(mot)
# collections.Counter : compteur specialise, tres pratique
from collections import Counter
c = Counter("mississippi")
print(c.most_common(2)) # [('i', 4), ('s', 4)]
# Dictionnaires imbriques et acces securise en profondeur
data = {"user": {"profile": {"name": "Alice"}}}
nom = data.get("user", {}).get("profile", {}).get("name", "inconnu")
# Comparaison de performance : dict vs list pour la recherche
grand_dict = {i: str(i) for i in range(1_000_000)}
print(999_999 in grand_dict) # O(1), contre O(n) pour une listeRésumé
dict.get(cle, defaut)évite lesKeyError,dict.setdefaultévite les vérifications manuelles.defaultdictetCounter(modulecollections) simplifient les patterns d'agrégation courants.- L'opérateur
|(Python 3.9+) fusionne deux dictionnaires sans muter les originaux.
Exercices pratiques
Mission : blinder un client météo qui plante en cascade
Objectif : Sécuriser l'accès à des données JSON imbriquées et remplacer un compteur manuel fragile par les bons outils de collections.
Contexte
Un client météo interne fait reponse["principal"]["temperature"] directement sur le JSON reçu d'une API externe. Le jour où l'API omet le champ "principal" pour une ville non reconnue, le service plante entièrement avec un KeyError non rattrapé, alors qu'il devrait simplement afficher "température inconnue".
Tu dois sécuriser cet accès, puis construire un compteur de villes les plus demandées sans boucle manuelle de vérification.