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.
age = "25"
age + 1
# TypeError: can only concatenate str (not "int") to strConcaté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.
| Situation | Ce qu'il faut faire |
|---|---|
| Une saisie ou un champ lu reste du texte | Convertir avec int ou float |
Une fonction a renvoyé None | Vérifier qu'elle contient bien un return |
| Mauvais nombre d'arguments | Comparer l'appel à la signature |
| Un objet n'est pas appelable | Des parenthèses posées sur autre chose qu'une fonction |
| Un type non ordonnable est trié | Fournir une clé de tri explicite |
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.
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.
# Le correctif habituel : convertir à la lecture
quantite = int(ligne["quantite"])
total = prix * quantiteConvertir 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.
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
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.
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.
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.