Aller au contenu
Développement high

Dictionnaires Python : stocker des données clé-valeur

35 min de lecture

logo python

Vous avez une liste de 1000 utilisateurs et vous cherchez celui dont l’email est “alice@example.com”. Avec une liste classique, vous devez parcourir chaque élément un par un. Avec un dictionnaire, l’accès est instantané : users["alice@example.com"].

Les dictionnaires Python sont des structures de données qui stockent des paires clé-valeur. Contrairement aux listes où vous accédez aux éléments par leur position (index 0, 1, 2…), les dictionnaires vous permettent d’accéder aux données par un nom significatif. C’est la structure idéale pour représenter des objets du monde réel : un utilisateur avec son nom, email et âge ; une configuration avec ses paramètres ; ou une réponse d’API.

Ce guide vous accompagne de la création de votre premier dictionnaire jusqu’aux techniques avancées comme les compréhensions et les structures imbriquées.

Ce guide couvre l’essentiel des dictionnaires Python, de la création à l’utilisation avancée. Chaque section est accompagnée d’exemples concrets que vous pouvez copier et tester immédiatement.

À la fin de ce guide, vous saurez :

  • Créer des dictionnaires vides ou pré-remplis — la base pour stocker vos données structurées
  • Accéder aux valeurs de manière sécurisée avec get() — éviter les crashs sur des clés manquantes
  • Ajouter, modifier et supprimer des éléments — les opérations CRUD quotidiennes
  • Parcourir les clés, valeurs ou les deux ensemble — traiter vos données efficacement
  • Transformer des données avec les compréhensions de dictionnaire — écrire du code concis et pythonique
  • Gérer des structures imbriquées (dictionnaires dans des dictionnaires) — manipuler des données JSON et API
  • Appliquer les bonnes pratiques pour un code robuste — éviter les pièges classiques

Un dictionnaire est une collection non ordonnée (depuis Python 3.7, l’ordre d’insertion est préservé) de paires clé-valeur. Chaque clé est unique et pointe vers une valeur.

# Un dictionnaire simple
personne = {
"nom": "Alice",
"age": 30,
"ville": "Paris"
}

Structure d'un dictionnaire Python : clés uniques (nom, age, email) pointant vers leurs valeurs

Chaque dictionnaire se compose de trois éléments fondamentaux que vous manipulerez constamment :

ÉlémentDescriptionExemple
CléIdentifiant unique (immuable)"nom", "age", 42
ValeurDonnée associée (tout type)"Alice", 30, [1, 2, 3]
PaireAssociation clé → valeur"nom": "Alice"

La clé joue le rôle d’étiquette : elle doit être unique dans le dictionnaire (impossible d’avoir deux clés identiques) et immuable (les chaînes, nombres et tuples fonctionnent, mais pas les listes).

La valeur peut être de n’importe quel type Python : une chaîne, un nombre, une liste, un autre dictionnaire, ou même une fonction. Cette flexibilité fait des dictionnaires une structure de données extrêmement polyvalente.

La paire clé-valeur est la brique de base : vous écrivez clé: valeur séparées par deux-points, et chaque paire est séparée des autres par une virgule.

Les listes et les dictionnaires sont les deux structures de données les plus utilisées en Python. Comprendre leurs différences vous permet de choisir la bonne structure selon votre besoin :

SituationListeDictionnaire
Accéder par positionliste[0]❌ Pas prévu pour ça
Accéder par nom/identifiant❌ Parcours nécessairedict["nom"]
Stocker des données hétérogènes nommées❌ Peu lisible✅ Idéal
Représenter un objet❌ Difficile✅ Naturel
Performance d’accèsO(n)O(1) en moyenne

Comparaison Liste vs Dictionnaire : l'index numérique de la liste ne donne pas de sens à la valeur, contrairement à la clé nommée du dictionnaire

Concrètement, voici comment ces différences se manifestent dans le code :

  • Accès par position vs par nom : avec une liste ["Alice", 30, "Paris"], vous devez vous souvenir que l’index 1 correspond à l’âge. Avec un dictionnaire, utilisateur["age"] est explicite.

  • Performance : pour une liste de 10 000 éléments, trouver une valeur peut nécessiter de parcourir les 10 000 éléments (complexité O(n)). Un dictionnaire accède directement à la valeur via sa clé, quelle que soit la taille (complexité O(1)).

  • Données hétérogènes : un dictionnaire exprime naturellement qu’un utilisateur a un nom (texte), un âge (nombre) et un statut actif (booléen). Une liste mélange ces types sans contexte.

# Dictionnaire vide
config = {}
# Dictionnaire avec des données
utilisateur = {
"nom": "Alice",
"email": "alice@example.com",
"age": 30,
"actif": True
}
# À partir de mots-clés
utilisateur = dict(nom="Alice", email="alice@example.com", age=30)
# À partir de tuples
utilisateur = dict([("nom", "Alice"), ("email", "alice@example.com")])
# À partir de deux listes avec zip()
cles = ["nom", "email", "age"]
valeurs = ["Alice", "alice@example.com", 30]
utilisateur = dict(zip(cles, valeurs))

Les clés doivent être immuables (non modifiables). Pourquoi cette contrainte ? Python utilise un mécanisme appelé hachage pour retrouver instantanément une valeur à partir de sa clé. Si la clé pouvait changer après son insertion, Python ne saurait plus où retrouver la valeur associée.

Types immuables utilisables comme clés :

# ✅ Clés valides
valide = {
"texte": "valeur", # str - le plus courant
42: "valeur", # int - utile pour les ID numériques
3.14: "valeur", # float - rare mais possible
(1, 2): "valeur", # tuple - utile pour les coordonnées
True: "valeur" # bool - peu recommandé (collision avec 1)
}

Types mutables interdits :

# ❌ Clés invalides (TypeError: unhashable type)
# invalide = {
# [1, 2]: "valeur", # liste → utilisez un tuple (1, 2)
# {"a": 1}: "valeur" # dict → utilisez un tuple de tuples
# }
utilisateur = {"nom": "Alice", "age": 30}
# Accès simple
nom = utilisateur["nom"]
print(nom) # Alice
# ❌ Erreur si la clé n'existe pas
# ville = utilisateur["ville"] # KeyError: 'ville'

La méthode get() évite les erreurs KeyError :

utilisateur = {"nom": "Alice", "age": 30}
# Retourne None si la clé n'existe pas
ville = utilisateur.get("ville")
print(ville) # None
# Retourne une valeur par défaut
ville = utilisateur.get("ville", "Non renseignée")
print(ville) # Non renseignée
# La clé existe : retourne sa valeur
nom = utilisateur.get("nom", "Inconnu")
print(nom) # Alice
utilisateur = {"nom": "Alice", "age": 30}
# Avec 'in'
if "email" in utilisateur:
print(utilisateur["email"])
else:
print("Email non renseigné")
# Avec 'not in'
if "ville" not in utilisateur:
utilisateur["ville"] = "Paris"
utilisateur = {"nom": "Alice"}
# Ajouter une nouvelle clé
utilisateur["email"] = "alice@example.com"
print(utilisateur) # {'nom': 'Alice', 'email': 'alice@example.com'}
# Modifier une clé existante
utilisateur["email"] = "alice.new@example.com"
print(utilisateur) # {'nom': 'Alice', 'email': 'alice.new@example.com'}
utilisateur = {"nom": "Alice"}
# Ajouter plusieurs clés
utilisateur.update({
"email": "alice@example.com",
"age": 30,
"ville": "Paris"
})
print(utilisateur)
# {'nom': 'Alice', 'email': 'alice@example.com', 'age': 30, 'ville': 'Paris'}
# update() écrase les valeurs existantes
utilisateur.update({"age": 31})
print(utilisateur["age"]) # 31

setdefault() ajoute une clé seulement si elle n’existe pas :

utilisateur = {"nom": "Alice"}
# La clé n'existe pas → elle est ajoutée
utilisateur.setdefault("ville", "Paris")
print(utilisateur) # {'nom': 'Alice', 'ville': 'Paris'}
# La clé existe déjà → rien ne change
utilisateur.setdefault("ville", "Lyon")
print(utilisateur) # {'nom': 'Alice', 'ville': 'Paris'}
utilisateur = {"nom": "Alice", "email": "alice@example.com", "age": 30}
# Supprimer et récupérer la valeur
email = utilisateur.pop("email")
print(email) # alice@example.com
print(utilisateur) # {'nom': 'Alice', 'age': 30}
# Valeur par défaut si la clé n'existe pas
ville = utilisateur.pop("ville", "Aucune")
print(ville) # Aucune (pas d'erreur)
utilisateur = {"nom": "Alice", "email": "alice@example.com"}
# Supprimer une clé
del utilisateur["email"]
print(utilisateur) # {'nom': 'Alice'}
# ❌ Erreur si la clé n'existe pas
# del utilisateur["ville"] # KeyError
utilisateur = {"nom": "Alice", "email": "alice@example.com", "age": 30}
# Supprime et retourne la dernière paire ajoutée
derniere = utilisateur.popitem()
print(derniere) # ('age', 30)
print(utilisateur) # {'nom': 'Alice', 'email': 'alice@example.com'}
utilisateur = {"nom": "Alice", "age": 30}
utilisateur.clear()
print(utilisateur) # {}

Contrairement aux listes où vous parcourez simplement les éléments dans l’ordre, un dictionnaire offre trois façons de le parcourir selon ce dont vous avez besoin : les clés seules, les valeurs seules, ou les deux ensemble. Choisir la bonne méthode rend votre code plus clair et plus efficace.

Quand vous avez besoin uniquement des identifiants (par exemple, pour vérifier quelles clés existent ou pour construire une liste de noms de champs) :

utilisateur = {"nom": "Alice", "age": 30, "ville": "Paris"}
# Par défaut, itérer sur un dict parcourt ses clés
for cle in utilisateur:
print(cle)
# nom
# age
# ville
# Explicitement avec keys() - même résultat, intention plus claire
for cle in utilisateur.keys():
print(cle)

Cas d’usage : lister les champs d’une configuration, vérifier la présence de certaines clés, générer des rapports sur la structure des données.

Quand vous n’avez pas besoin de savoir d’où vient chaque valeur (par exemple, pour calculer une somme ou trouver un maximum) :

utilisateur = {"nom": "Alice", "age": 30, "ville": "Paris"}
for valeur in utilisateur.values():
print(valeur)
# Alice
# 30
# Paris

Cas d’usage : calculer la somme des valeurs numériques, compter les occurrences, vérifier si une valeur particulière existe.

# Exemple pratique : calculer le total des scores
scores = {"Alice": 85, "Bob": 92, "Charlie": 78}
total = sum(scores.values())
print(f"Total: {total}") # Total: 255

C’est la méthode la plus courante car vous avez généralement besoin de contexte (la clé) pour traiter chaque valeur :

utilisateur = {"nom": "Alice", "age": 30, "ville": "Paris"}
for cle, valeur in utilisateur.items():
print(f"{cle}: {valeur}")
# nom: Alice
# age: 30
# ville: Paris

Cas d’usage : afficher un rapport formaté, transformer les données, filtrer selon des critères, exporter vers un autre format.

# Exemple pratique : formater pour l'affichage
config = {"host": "localhost", "port": 5432, "debug": True}
print("Configuration:")
for param, valeur in config.items():
print(f" • {param} = {valeur}")

Les compréhensions permettent de créer des dictionnaires de manière concise et expressive. C’est l’une des fonctionnalités les plus élégantes de Python : une seule ligne remplace souvent 4-5 lignes de boucle.

La syntaxe suit le pattern {clé: valeur for élément in iterable}. Lisez-la de droite à gauche : “pour chaque élément dans l’iterable, crée une paire clé-valeur”.

# Version longue avec boucle
carres = {}
for x in range(1, 6):
carres[x] = x**2
# Version compréhension (équivalente)
carres = {x: x**2 for x in range(1, 6)}
print(carres) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Décomposition : {x: x**2 for x in range(1, 6)}

  • for x in range(1, 6) → parcourt les nombres 1, 2, 3, 4, 5
  • x: x**2 → pour chaque x, crée la paire (x, x²)
  • {} → le tout dans un dictionnaire
# Garder seulement les nombres pairs
carres_pairs = {x: x**2 for x in range(1, 11) if x % 2 == 0}
print(carres_pairs) # {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}
prix_euros = {"pomme": 1.5, "banane": 0.8, "orange": 2.0}
# Convertir en dollars (taux 1.1)
prix_dollars = {fruit: prix * 1.1 for fruit, prix in prix_euros.items()}
print(prix_dollars) # {'pomme': 1.65, 'banane': 0.88, 'orange': 2.2}
# Filtrer les produits chers
produits_chers = {f: p for f, p in prix_euros.items() if p >= 1.5}
print(produits_chers) # {'pomme': 1.5, 'orange': 2.0}
original = {"a": 1, "b": 2, "c": 3}
inverse = {v: k for k, v in original.items()}
print(inverse) # {1: 'a', 2: 'b', 3: 'c'}
noms = ["Alice", "Bob", "Charlie"]
ages = [30, 25, 35]
# Avec zip() et compréhension
personnes = {nom: age for nom, age in zip(noms, ages)}
print(personnes) # {'Alice': 30, 'Bob': 25, 'Charlie': 35}

Les dictionnaires peuvent contenir d’autres dictionnaires, ce qui permet de représenter des structures de données complexes. C’est exactement ce que vous recevez quand vous interrogez une API REST ou lisez un fichier JSON.

  • Réponses d’API : {"status": "ok", "data": {"user": {...}, "permissions": [...]}}
  • Configuration : {"database": {"host": "...", "port": 5432}, "cache": {...}}
  • Données relationnelles : employés par département, produits par catégorie

Voici un exemple concret : une base de données d’employés où chaque ID pointe vers les informations de l’employé.

employes = {
"E001": {
"nom": "Alice",
"poste": "Développeuse",
"salaire": 45000
},
"E002": {
"nom": "Bob",
"poste": "Designer",
"salaire": 40000
}
}
# Accéder à une valeur imbriquée
print(employes["E001"]["nom"]) # Alice
print(employes["E002"]["salaire"]) # 40000
# Augmenter le salaire d'Alice
employes["E001"]["salaire"] = 48000
# Ajouter un nouvel attribut
employes["E001"]["departement"] = "IT"
for id_employe, infos in employes.items():
print(f"\n{id_employe}:")
for attribut, valeur in infos.items():
print(f" {attribut}: {valeur}")
# E001:
# nom: Alice
# poste: Développeuse
# salaire: 48000
# departement: IT
# E002:
# nom: Bob
# poste: Designer
# salaire: 40000
# ❌ Risqué si une clé intermédiaire n'existe pas
# ville = employes["E003"]["adresse"]["ville"] # KeyError
# ✅ Accès sécurisé chaîné
employe = employes.get("E003", {})
adresse = employe.get("adresse", {})
ville = adresse.get("ville", "Non renseignée")
print(ville) # Non renseignée

Fusionner des dictionnaires est une opération courante : combiner des configurations par défaut avec des options utilisateur, enrichir des données avec des informations complémentaires, ou consolider des résultats partiels.

Python propose trois syntaxes selon vos besoins.

Utilisez update() quand vous voulez modifier le dictionnaire original. C’est efficace en mémoire pour les gros dictionnaires.

base = {"nom": "Alice", "age": 30}
extra = {"ville": "Paris", "age": 31}
base.update(extra)
print(base) # {'nom': 'Alice', 'age': 31, 'ville': 'Paris'}
# ⚠️ Attention : 'age' est écrasé par la valeur de 'extra'

Cas d’usage : appliquer des options utilisateur sur une configuration par défaut.

base = {"nom": "Alice", "age": 30}
extra = {"ville": "Paris", "email": "alice@example.com"}
fusionne = {**base, **extra}
print(fusionne)
# {'nom': 'Alice', 'age': 30, 'ville': 'Paris', 'email': 'alice@example.com'}
# Les dicts originaux ne sont pas modifiés
print(base) # {'nom': 'Alice', 'age': 30}
base = {"nom": "Alice", "age": 30}
extra = {"ville": "Paris"}
# Union (nouveau dictionnaire)
fusionne = base | extra
print(fusionne) # {'nom': 'Alice', 'age': 30, 'ville': 'Paris'}
# Union en place
base |= extra
print(base) # {'nom': 'Alice', 'age': 30, 'ville': 'Paris'}

Voici les 10 méthodes que vous utiliserez le plus souvent. Gardez ce tableau sous le coude comme référence rapide :

MéthodeDescriptionExemple
get(clé, défaut)Accès sécuriséd.get("x", 0)
keys()Vue des clésfor k in d.keys()
values()Vue des valeursfor v in d.values()
items()Vue des pairesfor k, v in d.items()
pop(clé, défaut)Supprime et retourned.pop("x", None)
popitem()Supprime la dernière paired.popitem()
update(autre)Fusionned.update({"a": 1})
setdefault(clé, val)Ajoute si absentd.setdefault("x", 0)
clear()Vide le dictd.clear()
copy()Copie superficielled2 = d.copy()

Les plus utilisées au quotidien :

  • get() : indispensable pour éviter les crashs quand vous traitez des données externes (API, fichiers JSON, formulaires). Toujours préférer d.get("clé", valeur_defaut) à d["clé"] quand la clé peut être absente.

  • items() : la méthode de parcours standard. Dans 90% des cas où vous parcourez un dictionnaire, vous avez besoin à la fois de la clé et de la valeur.

  • update() : parfait pour fusionner des configurations ou enrichir des données. Attention : les clés existantes sont écrasées par les nouvelles valeurs.

  • pop() : utile pour extraire une valeur tout en la supprimant du dictionnaire (pattern courant dans les files de traitement).

Ces recommandations viennent de l’expérience collective de la communauté Python. Elles vous éviteront des bugs subtils et rendront votre code plus facile à maintenir.

Les abréviations font gagner quelques caractères mais rendent le code obscur. Dans 6 mois, vous ne vous souviendrez plus ce que signifie "n" ou "a".

# ❌ Peu lisible - économie de caractères mal placée
u = {"n": "Alice", "a": 30, "e": "alice@example.com"}
# ✅ Clair et maintenable - le code se lit comme de la prose
utilisateur = {"nom": "Alice", "age": 30, "email": "alice@example.com"}

Astuce : utilisez des noms qui répondent à la question “qu’est-ce que cette valeur représente ?” plutôt que des raccourcis cryptiques.

# Données venant d'une API ou d'un fichier
reponse = {"status": "ok", "data": {"user": "Alice"}}
# ✅ Robuste face aux champs manquants
user = reponse.get("data", {}).get("user", "Inconnu")
scores = {"Alice": 10, "Bob": 5, "Charlie": 15}
# ❌ Dangereux : RuntimeError possible
# for nom in scores:
# if scores[nom] < 10:
# del scores[nom]
# ✅ Créez une copie ou une liste des clés
for nom in list(scores.keys()):
if scores[nom] < 10:
del scores[nom]

4. Utilisez les compréhensions pour les transformations simples

Section intitulée « 4. Utilisez les compréhensions pour les transformations simples »
# ❌ Verbeux
resultat = {}
for x in range(5):
resultat[x] = x ** 2
# ✅ Concis et expressif
resultat = {x: x**2 for x in range(5)}
# ❌ Difficile à maintenir
data = {
"niveau1": {
"niveau2": {
"niveau3": {
"valeur": 42
}
}
}
}
# ✅ Envisagez des classes ou aplatissez la structure
from dataclasses import dataclass
@dataclass
class Config:
valeur: int = 42

Créez un programme de gestion de contacts avec les fonctionnalités suivantes :

  1. Créer un dictionnaire de contacts (nom → infos)
  2. Ajouter un nouveau contact avec email et téléphone
  3. Rechercher un contact par nom
  4. Modifier le téléphone d’un contact
  5. Supprimer un contact
  6. Lister tous les contacts

Structure attendue :

contacts = {
"Alice": {"email": "alice@example.com", "tel": "0612345678"},
"Bob": {"email": "bob@example.com", "tel": "0698765432"}
}

Voici les trois erreurs que tout développeur Python rencontre avec les dictionnaires. Comprendre leur cause permet de les éviter systématiquement.

C’est l’erreur la plus fréquente. Elle survient quand vous essayez d’accéder à une clé qui n’existe pas avec la syntaxe dict[clé].

Quand ça arrive : typiquement avec des données externes (réponse API, fichier JSON) où certains champs peuvent être absents.

d = {"nom": "Alice"}
# ❌ Erreur - la clé "age" n'existe pas
# print(d["age"]) # KeyError: 'age'
# ✅ Solution 1 : get() avec valeur par défaut
print(d.get("age")) # None (pas d'erreur)
print(d.get("age", 0)) # 0 (valeur par défaut)
# ✅ Solution 2 : vérifier l'existence avant d'accéder
if "age" in d:
print(d["age"])
else:
print("Âge non renseigné")

Règle : utilisez get() par défaut, réservez [] aux cas où l’absence de clé est une erreur de logique que vous voulez détecter.

# ❌ Les listes ne peuvent pas être des clés
# d = {[1, 2]: "valeur"} # TypeError: unhashable type: 'list'
# ✅ Utilisez un tuple
d = {(1, 2): "valeur"}
d = {"a": 1, "b": 2, "c": 3}
# ❌ RuntimeError possible
# for k in d:
# del d[k]
# ✅ Parcourez une copie
for k in list(d.keys()):
del d[k]

Si vous ne retenez que six choses de ce guide, retenez celles-ci. Ces règles couvrent 90% des situations que vous rencontrerez avec les dictionnaires :

  1. Dictionnaire = paires clé-valeur — accès par nom, pas par position. Idéal pour représenter des objets.

  2. Clés uniques et immuables — utilisez des strings, int, tuple. Jamais de listes ou dicts comme clés.

  3. get() pour l’accès sécurisé — évite les KeyError quand la clé peut être absente.

  4. items() pour parcourir — donne accès à la clé ET la valeur en même temps.

  5. Compréhensions pour transformer{k: v*2 for k, v in d.items()} est plus concis qu’une boucle.

  6. Ne jamais modifier pendant le parcours — créez une copie des clés avec list(d.keys()).

Utilisez cette checklist pour valider votre maîtrise des dictionnaires. Cochez chaque point une fois que vous êtes capable de le faire sans consulter la documentation. Si un point vous bloque, revenez à la section correspondante du guide.

Ces compétences sont indispensables pour tout travail avec les dictionnaires :

  • Créer un dictionnaire avec {} ou dict()
  • Accéder aux valeurs avec [] et get()
  • Vérifier l’existence d’une clé avec in
  • Ajouter et modifier des éléments

Ces opérations reviennent constamment dans le code du quotidien :

  • Supprimer avec pop(), del, clear()
  • Parcourir avec keys(), values(), items()
  • Fusionner avec update() ou |

Ces techniques vous permettent d’écrire du code plus élégant et de gérer des données complexes :

  • Utiliser les compréhensions de dictionnaire
  • Gérer les structures imbriquées
  • Accès sécurisé aux données imbriquées