Définition
Une fonction Python attend souvent plus qu'un type : elle attend une valeur qui a du sens. Donnez-lui une chaîne là où elle en réclame une, elle ne se plaindra pas, le type est bon. Mais si le contenu ne correspond à rien qu'elle sache traiter, elle doit le signaler autrement : c'est le rôle de cette exception, ValueError.
age = int("douze") # on voulait un nombre, on tape un mot
# ValueError: invalid literal for int() with base 10: 'douze'La chaîne "douze" est bien une chaîne, et int accepte parfaitement les chaînes en argument. Le problème n'est pas là : ce texte précis ne contient simplement pas de nombre reconnaissable. Cette nuance change l'endroit où chercher quand quelque chose casse. Passer une liste à la même fonction aurait produit une TypeError à la place, puisque le reproche aurait alors porté sur la nature de l'argument, et non sur son contenu.
La différence avec TypeError
Confondre les deux erreurs fait chercher au mauvais endroit pendant de longues minutes, parce qu'on corrige le type d'une donnée qui était déjà du bon type. Trois appels suffisent à voir où passe la frontière entre les deux.
| Appel | Résultat | Raison |
|---|---|---|
int("12") | Aucune erreur | Une chaîne qui contient bien un nombre |
int("douze") | ValueError | Le bon type, un contenu inconvertible |
int([1, 2]) | TypeError | Un type que la fonction ne sait pas traiter |
La distinction devient presque un réflexe une fois ces trois cas en tête : si corriger le contenu de la valeur suffit à faire passer l'appel, c'est une ValueError ; s'il faut changer sa nature, c'est une TypeError. Le traceback affiche toujours la valeur fautive sur sa dernière ligne, inutile de la deviner.
Où elle apparaît le plus souvent
La conversion d'une saisie arrive largement en tête, pour une raison simple : un input renvoie toujours du texte, quoi que la personne ait tapé. Convertir ce texte avec int ou float revient à faire dépendre tout le programme de ce qui se trouve réellement au clavier à cet instant.
Le dépaquetage vient ensuite, pour une raison voisine. Une affectation multiple exige un nombre d'éléments exact de part et d'autre du signe égal, et un split sur une ligne mal formée n'en fournit pas toujours autant.
nom, prenom = "Dupont".split(";") # la ligne attendue contenait un point-virgule
# ValueError: not enough values to unpack (expected 2, got 1)Suivent les recherches qui échouent, comme liste.index(valeur) ou liste.remove(valeur) appelées sur un élément absent, puis les fonctions mathématiques sollicitées hors de leur domaine, comme une racine carrée sur un nombre négatif. Ces situations partagent la même origine : la donnée vient de l'extérieur du programme, et personne ne l'a vérifiée avant de s'en servir.
La rattraper, ou la lever soi-même
Le rattrapage se fait avec try et except, en visant l'erreur précise plutôt qu'une classe trop générale.
while True:
saisie = input("Votre age : ")
try:
age = int(saisie)
break
except ValueError:
print("Chiffres uniquement, merci.")Écrire except Exception à la place de except ValueError semble plus prudent, mais ça avale aussi les fautes de frappe dans un nom de variable ou une clé absente. Le programme continue de tourner en silence, et le vrai bug se découvre bien plus tard, ailleurs.
L'autre moitié du travail consiste à lever la ValueError soi-même, dans ses propres fonctions, avec raise. Une fonction qui reçoit une durée négative ou un mois valant 13 a tout intérêt à refuser immédiatement, avec un message qui nomme la valeur reçue, plutôt qu'à laisser une donnée absurde se propager sur dix appels.
def appliquer_remise(prix, pourcentage):
if not 0 <= pourcentage <= 100:
raise ValueError(f"pourcentage hors bornes : {pourcentage}")
return prix * (1 - pourcentage / 100)La vraie correction est en amont
Une ValueError rattrapée en silence n'a rien corrigé du tout, elle a seulement caché le symptôme. Elle signale presque toujours qu'une donnée est entrée dans le programme sans avoir été contrôlée, et l'endroit où elle éclate est rarement celui où le problème est né.
La méthode qui fait gagner le plus de temps consiste à valider à la frontière, au moment où le texte est saisi, où le fichier est lu, où la réponse d'un service extérieur est décodée. Passé ce point, le reste du programme travaille sur des données dont la forme est garantie, et l'erreur disparaît des couches profondes du code, là où elle coûte le plus cher à comprendre.
Questions fréquentes
Comment convertir une saisie sans risquer l'erreur ?
Il n'existe pas de conversion tolérante en Python : int réussit ou lève. La seule approche fiable consiste à entourer l'appel d'un try, ou à valider la chaîne au préalable avec une méthode comme isdigit, qui ne gère toutefois ni le signe négatif ni les décimales.
Pourquoi mon dépaquetage échoue-t-il sur une seule ligne du fichier ?
Parce que cette ligne précise n'a pas le format attendu : un séparateur manquant, un champ vide en fin de ligne, ou une ligne blanche finale. Fixer un nombre maximal de coupes avec split(";", 1), ou tester la longueur du résultat avant d'affecter, règle le problème pour de bon.
Faut-il lever cette erreur ou créer sa propre exception ?
Tant que le problème se résume à une valeur refusée, l'erreur standard reste le meilleur choix : tout le monde la connaît et sait la rattraper. Une exception maison ne se justifie que si l'appelant doit distinguer ce cas précis des autres pour réagir différemment, ce qui est plus rare qu'il n'y paraît.