TypeError en Python : d'où vient l'erreur et comment la corriger

TypeError signale une opération appliquée à un type qui ne la supporte pas, comme additionner un nombre et une chaîne.
5 min de lecture
Believemy logo

Définition

Écrire du Python donne l'impression d'une grande liberté : aucune variable n'a de type déclaré, rien n'oblige à y penser avant de lancer le programme. Cette liberté trouve sa limite au moment où deux valeurs de nature différente se croisent dans une même opération, une addition, une comparaison, une concaténation. Python doit alors choisir entre deviner ce que vous vouliez faire ou s'arrêter net. Il s'arrête, et c'est ce refus que porte TypeError : une opération appliquée à un type qui ne la supporte pas.

Ce choix n'est pas un manque de souplesse, c'est un parti pris. Python est à typage dynamique, il ne demande pas de déclarer les types à l'avance, mais il reste fort : il ne les mélange jamais de lui-même, contrairement à d'autres langages qui auraient tranché à votre place.

PYTHON
age = "25"
age + 1

# TypeError: can only concatenate str (not "int") to str

Concaténer aurait donné « 251 », convertir aurait donné 26. Aucune des deux réponses n'est plus juste que l'autre, ce qui est justement le signe qu'aucune conversion automatique n'aurait de sens ici. Mieux vaut une erreur explicite à la ligne où le problème se pose qu'un résultat silencieusement faux découvert trois semaines plus tard.


Les causes, par ordre de fréquence

Le tableau suivant les classe par fréquence observée, avec le réflexe qui règle chacune.

SituationCe qu'il faut faire
Une saisie ou un champ lu reste du texteConvertir avec int ou float
Une fonction a renvoyé NoneVérifier qu'elle contient bien un return
Mauvais nombre d'argumentsComparer l'appel à la signature
Un objet n'est pas appelableDes parenthèses posées sur autre chose qu'une fonction
Un type non ordonnable est triéFournir une clé de tri explicite
Bon à savoir

Un cas particulier d'objet non appelable revient souvent : nommer une variable comme un type intégré, par exemple list = [1, 2, 3], puis appeler list(range(3)) plus loin. Python ne proteste qu'au premier appel qui suit, une fois que list ne désigne plus le type mais votre variable.

Le message « NoneType object is not subscriptable » mérite d'être reconnu au premier coup d'œil : il signifie qu'on essaie d'indexer None, comme s'il s'agissait d'une liste ou d'un dictionnaire. La tentation naturelle est de corriger la ligne que Python désigne, et c'est presque toujours la mauvaise piste.

Attention

La ligne signalée par Python montre où l'erreur éclate, pas où elle est née. Pour NoneType is not subscriptable, la vraie cause se trouve presque toujours une fonction plus haut, dans celle qui a renvoyé None au lieu de la valeur attendue.


Le message dit tout

Reste ensuite à savoir quoi corriger. Contrairement à beaucoup d'erreurs Python qui se contentent de nommer le problème, TypeError va plus loin : il nomme les deux types en cause. « unsupported operand type(s) for +: 'int' and 'str' » donne l'opération, le type de gauche et celui de droite. Il ne reste qu'à repérer lequel des deux ne devrait pas être là.

Le correctif suit alors presque toujours le même chemin : convertir la valeur suspecte avant qu'elle ne rencontre l'opération qui échoue, plutôt qu'au moment même du calcul.

PYTHON
# Le correctif habituel : convertir à la lecture
quantite = int(ligne["quantite"])
total = prix * quantite

Convertir dès l'entrée, plutôt qu'au moment du calcul, change quelque chose de concret : la validation se fait une seule fois, à un seul endroit, au lieu d'être recopiée partout où la donnée circule ensuite.


Le cas des arguments

Les TypeError ne viennent pas toutes d'un calcul mal assorti. Une bonne moitié concerne non pas une valeur, mais un appel de fonction. Le message y est encore plus précis : il donne le nom de la fonction, le nombre d'arguments attendus, et souvent celui qui manque nommément.

PYTHON
def envoyer(destinataire, sujet, corps):
    ...

envoyer("a@b.fr", "Bonjour")

# TypeError: envoyer() missing 1 required positional argument: 'corps'

Sur une méthode, ce même message cache un piège classique. self compte comme un argument, alors que personne ne l'écrit à l'appel : c'est lui que la méthode reçoit en premier. Un message annonçant deux arguments attendus pour un seul fourni ne signale donc presque jamais un oubli de votre part, mais un appel fait sur la classe elle-même plutôt que sur une instance, ce qui prive Python du self qu'il attendait.


Questions fréquentes

Question

Quelle différence avec ValueError ?

La distinction tient à ce qui cloche. TypeError porte sur la nature de la donnée, ValueError sur son contenu, le type restant correct. int("abc") lève une ValueError : la chaîne est un type acceptable pour int(), seul son contenu ne se convertit pas. int([1, 2]) lève au contraire un TypeError, parce qu'une liste n'est simplement pas le genre de chose qu'int() sait traiter.

Question

Les annotations de type l'empêchent-elles ?

Non, et c'est une confusion fréquente chez qui débute. Python ignore les annotations au moment de l'exécution : elles documentent l'intention et nourrissent des outils d'analyse. C'est cet outillage, lancé avant la mise en production, qui repère l'incohérence de type en amont, pas l'interpréteur.

Question

Pourquoi mon addition de deux nombres échoue-t-elle ?

Le plus souvent, parce que l'un des deux n'est pas vraiment un nombre, même si tout porte à le croire. Un champ lu dans un fichier, une réponse d'interface distante ou une saisie utilisateur arrivent presque toujours en texte, même quand ce texte ne contient que des chiffres. Rien dans leur apparence ne trahit leur vrai type : seule l'erreur le révèle.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.