Dictionnaires — des paires de clé et de valeur
Stocker et récupérer des valeurs par nom, éviter KeyError avec .get(), ajouter et supprimer des clés, parcourir avec .items(), et pourquoi une liste ne peut pas être une clé.
Comment voulez-vous apprendre ?
Le problème que nous résolvons
Les notes de trois personnes doivent être stockées — en sachant à qui appartient chaque note.
Les listes n'offrent qu'une seule façon : deux listes, côte à côte.
names = ["rafi", "ahmed", "bilal"]
marks = [72, 45, 90]
print(names[1], marks[1])ahmed 45Cela fonctionne. Et tout l'édifice repose sur votre discipline, sans aucun secours de Python.
Vous ajoutez quelqu'un à names et oubliez marks ? Les deux listes ont maintenant des longueurs différentes, et dès lors names[2] et marks[2] désignent deux personnes différentes. Python ne motera pas. Vous triez names ? La relation casse complètement, et en silence.
Voilà le fond du problème : la relation entre les deux choses n'est écrite nulle part. La relation n'est que « le même index » — une coïncidence qui peut casser à tout moment.
Un dictionnaire écrit la relation dans le code. Des noms au lieu d'index, et la paire tenue ensemble en un seul endroit.
À la fin de ce chapitre, vous pourrez
- Construire un dictionnaire et récupérer une valeur par sa clé
- Éviter un
KeyErroravec.get(), et savoir quand l'un ou l'autre convient - Ajouter, modifier et supprimer des clés
- Parcourir un dictionnaire avec
.keys(),.values()et.items() - Expliquer pourquoi une liste ne peut pas être une clé mais un tuple peut
Prérequis : Tuples et déballage.
Un dictionnaire — des paires de clé et de valeur
Des accolades, avec des paires key: value à l'intérieur :
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(marks)
print(marks["ahmed"])
print(len(marks)){'rafi': 72, 'ahmed': 45, 'bilal': 90}
45
3marks["ahmed"] — un nom, pas un index. Et le nom est là, dans le code, donc six mois plus tard il dit encore ce qui est demandé.
len() compte les paires : trois.
Un mot sur l'ordre. Depuis Python 3.7, un dictionnaire retient l'ordre dans lequel les choses ont été mises, et les affiche dans cet ordre. Mais un dictionnaire sert à chercher des choses par nom, pas par position — écrivezmarks[0]et Python cherchera une clé qui soit le nombre zéro, n'en trouvera pas, et s'arrêtera surKeyError: 0.
Une clé qui n'est pas là — KeyError et .get()
marks = {"rafi": 72}
print(marks["dia"])KeyError: 'dia'Un KeyError signifie « il n'y a pas de clé portant ce nom ». La même erreur sur une liste donnait un IndexError — un autre nom pour le même genre de problème.
Pour obtenir une réponse sûre au lieu d'un arrêt, utilisez .get() :
marks = {"rafi": 72}
print(marks.get("dia"))
print(marks.get("dia", 0))
print(marks.get("rafi", 0))None
0
72Le second argument dit « donne-moi ceci s'il manque ». Sans lui, vous obtenez None.
Lequel, et quand ? Si la clé devrait être là, écrivez marks["rafi"] — son absence est un bug, et le KeyError vous le remet tout de suite. Si l'absence de la clé est normale, utilisez .get(). Se servir de .get() partout laisse passer les mauvaises données en silence, et les mauvaises données, c'est ce que vous essayez de trouver.
Ajouter, modifier, supprimer
marks = {"rafi": 72}
marks["dia"] = 88
marks["rafi"] = 75
print(marks)
del marks["rafi"]
print(marks){'rafi': 75, 'dia': 88}
{'dia': 88}La même ligne fait deux choses : si la clé manque, elle est ajoutée ; si elle existe, elle est remplacée. Il n'y a pas de méthode « ajouter » à part.
Et de là découle une règle importante — une clé ne peut apparaître qu'une fois :
marks = {"rafi": 72, "rafi": 90}
print(marks){'rafi': 90}Pas d'erreur. La seconde a effacé la première en silence. Cela arrive régulièrement lors du chargement des données depuis un fichier, et c'est une façon courante dont des enregistrements disparaissent.
in cherche dans les clés, pas dans les valeurs
marks = {"rafi": 72, "ahmed": 45}
print("rafi" in marks)
print(72 in marks)True
False72 est bien là en tant que valeur, mais in ne regarde que parmi les clés. Pour chercher dans les valeurs, il faut le dire : 72 in marks.values().
Le parcourir, de trois façons
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)
print("---")
for name, mark in marks.items():
print(name, mark)
print("---")
print(list(marks.keys()))
print(list(marks.values()))rafi
ahmed
---
rafi 72
ahmed 45
---
['rafi', 'ahmed']
[72, 45]La première mérite qu'on s'y arrête : for name in marks vous donne les clés, pas les valeurs. Cela surprend tout le monde la première fois.
Quand vous voulez les deux, .items() rend un tuple à chaque tour — et name, mark le déballe. C'est là que le déballage du chapitre précédent prouve son utilité.
Pour calculer sur les valeurs, .values() :
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(sum(marks.values()))
print(max(marks.values()))
print(round(sum(marks.values()) / len(marks), 2))207
90
69.0Ce qui peut être une clé
Pour être une clé, une chose doit être immuable. Le texte, les nombres et les tuples conviennent tous. Pas une liste :
d = {}
d[["a", "b"]] = 1TypeError: unhashable type: 'list'Ce mot « unhashable » vaut la peine d'être déplié. Un dictionnaire trouve les choses si vite parce qu'il calcule un nombre à partir de chaque clé et se sert de ce nombre pour aller directement au bon endroit. Si la clé pouvait changer ensuite, ce nombre serait faux et l'entrée serait perdue. Python refuse donc que quoi que ce soit de modifiable soit une clé.
Un tuple ne peut pas changer, ce qui en fait une excellente clé :
grid = {}
grid[(0, 0)] = "start"
grid[(1, 2)] = "tree"
print(grid)
print(grid[(1, 2)]){(0, 0): 'start', (1, 2): 'tree'}
treeLe chapitre précédent appelait l'immutabilité d'un tuple un avantage. Voici la première preuve pratique.
Le motif le plus utile qui soit — le comptage
words = ["pen", "bag", "pen", "ink", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts){'pen': 3, 'bag': 1, 'ink': 1}Lisez la ligne de l'intérieur : counts.get(word, 0) dit « donne-moi le comptage de ce mot pour l'instant, et zéro s'il n'en a pas encore », puis un est ajouté et le résultat remis à sa place.
Le défaut de .get() fait tout le travail ici — sans lui, chaque nouveau mot lèverait un KeyError et il faudrait if word in counts: et deux branches. Vous écrirez ce motif en une ligne de nombreuses fois.
Un exemple complet
marks.py :
# A dictionary keeps a name attached to its value
marks = {"rafi": 72, "ahmed": 45, "bilal": 90, "dia": 88}
print("Everyone:")
for name, mark in marks.items():
status = "pass" if mark >= 40 else "fail"
print(f" {name:<8} {mark:>3} {status}")
print()
print("Count :", len(marks))
print("Total :", sum(marks.values()))
print("Average :", round(sum(marks.values()) / len(marks), 2))
print("Highest :", max(marks.values()))
best = max(marks, key=marks.get)
print("Best :", best, marks[best])
print("Has dia :", "dia" in marks)
print("Missing :", marks.get("nadia", "not recorded"))
marks["dia"] = 91
marks["nadia"] = 60
del marks["ahmed"]
print()
print("After edits:", marks)Everyone:
rafi 72 pass
ahmed 45 pass
bilal 90 pass
dia 88 pass
Count : 4
Total : 295
Average : 73.75
Highest : 90
Best : bilal 90
Has dia : True
Missing : not recorded
After edits: {'rafi': 72, 'bilal': 90, 'dia': 91, 'nadia': 60}Trois choses à remarquer.
status = "pass" if mark >= 40 else "fail" est une façon d'écrire une condition sur une ligne, lue du milieu vers l'extérieur : « "pass" si mark >= 40, sinon "fail" ». Le if/else en quatre lignes du chapitre neuf choisit ici une seule valeur, donc une ligne suffit. Écrivez un vrai if pour une vraie décision — cette forme ne sert qu'à choisir entre deux valeurs.
max(marks, key=marks.get) est le key= du chapitre treize qui fait un nouveau travail. (Ici key= est le nom d'un argument de max, pas une clé de dictionnaire.) max(marks) donnerait la plus grande clé, c'est-à-dire le dernier nom dans l'ordre alphabétique. key=marks.get dit de comparer chaque clé par sa valeur à la place, ce qui donne le nom ayant la meilleure note. Remarquez que obtenir la meilleure note est facile (max(marks.values())) ; savoir à qui elle appartient demande cette astuce.
L'ordre sur la dernière ligne. dia existait déjà, donc sa valeur a changé mais pas sa position. nadia est nouveau, donc il a été ajouté à la fin. Et ahmed n'est plus là. Un dictionnaire retient l'ordre d'insertion, et remplacer une valeur ne le bouscule pas.
En cas de problème
KeyError: 'dia' Il n'y a pas de clé portant ce nom. Vérifiez l'orthographe et la casse — "Rafi" et "rafi" sont deux clés différentes. Les clés lues depuis un fichier portent souvent une espace finale ; print(list(d.keys())) les entoure de guillemets pour qu'elle se voie. Si l'absence de la clé est normale, utilisez .get().
TypeError: unhashable type: 'list' Une tentative d'utiliser une liste comme clé. Utilisez un tuple — tuple(my_list) en fabrique un.
for x in my_dict ne me donne pas les valeurs Et c'est normal — cela donne les clés. Pour les valeurs, utilisez .values(), et pour les deux .items().
Deux clés étaient identiques et un enregistrement a disparu Une clé n'existe qu'une fois, et la suivante a remplacé la précédente en silence. Pour garder plusieurs enregistrements sous un même nom, faites de la valeur une liste — le sujet du chapitre dix-sept.
KeyError: 0 Le code a écrit my_dict[0], demandant le premier élément comme s'il s'agissait d'une liste. Un dictionnaire n'a pas de positions ; 0 est cherché comme clé, et quand une telle clé n'existe pas, vous obtenez KeyError: 0. Si vous avez vraiment besoin de la première paire, écrivez list(my_dict.items())[0].
.sort() ne fonctionne pas sur un dictionnaire Exact — les dictionnaires n'ont pas de .sort(). sorted(d.items()) renvoie une liste triée, ce qui est généralement ce que vous vouliez.
Étape 4 sur 6 — Prédire
Vérifiez votre compréhension
Une boucle parcourt directement un dictionnaire. Que s'affiche-t-il ?
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)- Arafi 72 ahmed 45
- Brafi ahmed
- C72 45
- D('rafi', 72) ('ahmed', 45)
On demande une clé qui n'est pas là. Que se passe-t-il, et quelle est l'alternative sûre ?
marks = {"rafi": 72}
print(marks["dia"])- AIl affiche `None`
- BIl affiche une ligne vide
- CUn `KeyError` — l'alternative sûre est `marks.get("dia")`, qui donne `None`
- DUn `IndexError`
Le motif de comptage. Que s'affiche-t-il ?
words = ["pen", "bag", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)- AUn `KeyError`
- B{'pen': 2, 'bag': 1}
- C{'pen': 1, 'bag': 1}
- D{'pen': 3, 'bag': 1}
Répondre nécessite un compte
Connectez-vous pour vérifier vos réponses
Les questions sont ci-dessus, et les résoudre de tête est ce qui compte vraiment. Connectez-vous pour voir les réponses, les explications et les indices à trois niveaux.
À vous de jouer
Écrivez un fichier appelé stock.py contenant un dictionnaire d'au moins six produits — le nom du produit comme clé, le prix comme valeur.
Ensuite :
- Affichez chaque produit et son prix en colonnes alignées
- Affichez le total, la moyenne, le prix le plus élevé et le plus bas
- Trouvez le nom du produit le plus cher, pas seulement le prix
- Ajoutez un produit, modifiez un prix, supprimez un produit, puis affichez le tout à nouveau
- Utilisez
.get()pour demander un produit qui n'existe pas, avec un message comme valeur par défaut
Puis écrivez un programme de comptage : mettez plusieurs mots dans une liste, certains répétés, et comptez combien de fois chacun apparaît en utilisant le motif counts.get(word, 0) + 1.
Enfin, cassez-le exprès : demandez une clé manquante avec des crochets, et essayez d'utiliser une liste comme clé. Deux erreurs différentes reviendront — expliquez-vous laquelle des deux c'est, et pourquoi.
Étape 6 sur 6
Défi — le quiz du chapitre
Dix questions, de la plus facile à la plus difficile. Les dernières sont difficiles exprès.
Connectez-vous pour faire le quiz