KeyError Python : clé absente, get() ou in ?
Corrige une KeyError Python en vérifiant la clé cherchée. Distingue clé obligatoire, valeur par défaut et compteur, sans confondre absence, zéro et None.
Ton programme s'arrête sur KeyError: 'coffee'. Le message te donne la clé recherchée, mais remplacer tous les crochets par get() peut cacher le problème au lieu de le corriger.
En bref
Une KeyError lors d'un accès à un dictionnaire Python ordinaire signifie que la clé demandée est absente. Vérifie la valeur exacte de cette clé et son type, puis choisis selon le contrat : d[key] si elle doit exister, d.get(key, default) si son absence est prévue, ou key in d pour tester sa présence. Une valeur 0 ou None peut appartenir à une clé présente ; elle ne prouve pas une absence.
Que signifie la clé affichée dans KeyError ?
Dans stock["coffee"], Python cherche la clé "coffee", pas la première valeur disponible. Ici, seule "tea" existe.
stock = {"tea": 0}print(stock["coffee"])$ python3 missing.pyTraceback (most recent call last): File "/home/student/lab/missing.py", line 2, in <module> print(stock["coffee"]) ~~~~~^^^^^^^^^^KeyError: 'coffee'Lis la dernière ligne du traceback pour connaître la clé, puis la ligne de ton fichier pour trouver l'accès qui échoue. Avec plusieurs accès sur une ligne, sépare-les temporairement : tu dois identifier quel dictionnaire est consulté à ce moment-là.
Le diagnostic concerne ici les dictionnaires ordinaires. D'autres objets peuvent aussi lever une KeyError, et une sous-classe de dictionnaire peut définir un comportement particulier pour les clés absentes.
Pourquoi la clé semble-t-elle pourtant présente ?
La chaîne "tea " contient un espace final. Ce n'est pas la clé "tea". Affiche sa représentation avec repr() pour voir les délimiteurs, puis compare les types et les clés du dictionnaire concerné.
stock = {"tea": 0, "1": 8}key = "tea "print(repr(key), type(key).__name__)print(list(stock))print(key in stock)print("tea" in stock)print(1 in stock, "1" in stock)$ python3 inspect.py'tea ' str['tea', '1']FalseTrueFalse TrueVérifie aussi la casse et l'objet consulté : deux dictionnaires peuvent avoir des contenus différents. Une chaîne "1" et l'entier 1 sont des clés différentes. Pour une liste, l'indice absent provoque plutôt une IndexError ; un dictionnaire peut tout à fait avoir une clé entière.
Ne supprime pas systématiquement les espaces et ne convertis pas toutes les clés en minuscules. Ces transformations ont un sens seulement si ton format d'entrée les autorise. Dans un comptage de caractères sensible à la casse, elles changeraient les données.
Faut-il utiliser get(), in ou les crochets ?
Choisis ce que signifie une clé absente dans ton programme. Si un champ obligatoire manque, laisser remonter l'erreur permet de détecter une entrée invalide. Si tu veux l'expliquer à l'appelant, intercepte précisément KeyError autour de l'accès concerné. Un except: englobant toute la fonction cacherait aussi d'autres erreurs.
Que signifie une clé absente ?
Champ obligatoire
d[key]
Une absence doit être détectée et corrigée dans les données ou chez l'appelant.
Valeur facultative
d.get(key, default)
Le programme a une valeur de repli définie pour ce cas.
Présence à tester
key in d
Le test porte sur la clé, même si sa valeur vaut zéro ou None.
get() lit sans ajouter de clé. La valeur de repli est utilisée seulement si la clé manque.
counts = {"a": 2, "b": 1}print(counts.get("c", 0))print("c" in counts)print(counts)print(counts.get("a", 0))$ python3 lookup.py0False{'a': 2, 'b': 1}2Pour enregistrer une valeur, il faut une affectation comme celle du compteur plus bas.
Pourquoi if not d.get(key) peut-il se tromper ?
Ce test porte sur la valeur renvoyée. Il est vrai pour 0, None, False ou une chaîne vide, même lorsque la clé existe.
stock = {"tea": 0, "coffee": None}for key in ("tea", "coffee", "milk"): print(key, key in stock, stock.get(key), not stock.get(key))$ python3 presence.pytea True 0 Truecoffee True None Truemilk False None True"tea" existe et vaut zéro. "coffee" existe avec une valeur inconnue représentée par None. "milk" manque. Si ton besoin est de distinguer ces situations, teste l'appartenance.
Pour récupérer une valeur en une seule lecture et distinguer une absence d'un None stocké, une sentinelle privée convient aussi :
missing = object()stock = {"coffee": None}for key in ("coffee", "milk"): value = stock.get(key, missing) print(key, value is missing, value is None)$ python3 sentinel.pycoffee False Truemilk True FalseCompare la sentinelle avec is. L'objet créé pour ce test n'est aucune des valeurs déjà placées dans ce dictionnaire.
Comment initialiser un compteur sans KeyError ?
Dans un dictionnaire vide, counts[char] += 1 doit d'abord lire une valeur qui n'existe pas encore. Pour un compteur, le contrat naturel est un nombre initial de zéro, suivi d'une affectation.
def frequencies(text): counts = {} for char in text: counts[char] = counts.get(char, 0) + 1 return counts
for prefix in ("", "a", "ab", "aba"): print(repr(prefix), frequencies(prefix))$ python3 counter.py'' {}'a' {'a': 1}'ab' {'a': 1, 'b': 1}'aba' {'a': 2, 'b': 1}Sur "aba", la lecture de "a" donne zéro au premier passage, puis un au dernier. L'affectation crée la clé la première fois et remplace sa valeur la suivante.
Étape 1 sur 3
Après le premier a
- char
- a
- a1créée
- babsente
get renvoie zéro, puis l'affectation enregistre un.
Lire toutes les étapes
1. Après le premier a
- char
- a
- a1créée
- babsente
get renvoie zéro, puis l'affectation enregistre un.
2. Après b
- char
- b
- a1
- b1créée
Une deuxième clé est ajoutée ; le compte de a ne change pas.
3. Après le dernier a
- char
- a
- a2incrémentée
- b1
get lit un ; l'affectation enregistre deux sous la même clé.
Le dictionnaire vide est créé à chaque appel de frequencies. Une chaîne vide donne un dictionnaire vide. Chaque caractère est conservé tel quel : les espaces et la casse comptent.
same_character_bag
Compare les fréquences de deux chaînes. L'exercice demande les mêmes caractères avec les mêmes nombres d'occurrences ; les espaces et la casse comptent, et deux chaînes vides correspondent. Un ensemble de caractères seul perdrait les fréquences.
Pour ordonner ensuite des résultats, consulte le tri Python sur plusieurs critères. Si une modification apparaît dans plusieurs structures, le guide des copies de listes imbriquées traite cet autre mécanisme.
Questions fréquentes
get() ajoute-t-il la clé absente ?
Non. Il renvoie le repli et laisse le dictionnaire inchangé. Le compteur ajoute ou met à jour la clé avec son affectation.
Pourquoi get(key, 0) renvoie-t-il None ?
Si la clé existe avec la valeur None, Python renvoie cette valeur. Le repli 0 s'applique seulement à une clé absente.
Faut-il intercepter toutes les KeyError ?
Intercepte une absence seulement quand ton programme sait la traiter. Pour une clé obligatoire, corrige l'entrée ou signale le champ manquant ; un repli silencieux peut produire un résultat faux.
in cherche-t-il parmi les valeurs ?
Sur un dictionnaire, key in d teste les clés. Pour rechercher une valeur, il faut consulter ses valeurs, ce qui répond à une autre question.
Sources
