Salut.
Après quelques mois d'existence de ce forum, on peut faire le constat que pas mal de topics de résolution de problèmes tournent autour de la mauvaise compréhension des exceptions que Python peut lever. Je vous propose que l'on recense dans ce topic, une liste des erreurs très courantes que nous observons chez les débutants, ainsi que des explications claires de manière à les aider à apprendre de leurs erreurs.
Il serait possible, ainsi, de transformer ce topic en une sorte de FAQ : « Mon programme plante. Quelle est mon erreur ? Comment la corriger ? », ayant pour optique de guider les débutants vers des réactions plus matures (et d'éviter les bazillions de doublons sur le forum), et les faire gagner en autonomie.
Avant de recenser ces erreurs, commençons par apprendre à les « lire ».
Anatomie d'une exception Python
Lorsqu'un programme Python plante, c'est en général (si ce n'est toujours…) en raison d'une exception non gérée. Voici le genre d'affichage que cela produit dans la console :
Traceback (most recent call last):
File "test.py", line 6, in <module>
test()
File "test.py", line 3, in test
print table[4]
IndexError: list index out of range
Ces erreurs affichées par Python se lisent de bas en haut.
La ligne la plus importante : la dernière.
Examinons la dernière ligne de l'exemple ci-dessus.
IndexError: list index out of range
Sur cette ligne, nous avons 2 informations.
La première, c'est le type d'exception qui s'est produit. Dans cet exemple, il s'agit d'une IndexError. C'est en fonction de ces types d'erreurs que les exceptions seront classées dans ce topic.
La seconde, c'est le message contenu dans cette exception. Au travers de ce message, Python nous donne des indications sur ce qui a provoqué l'erreur.
Les lignes précédentes : le « traceback »
Les lignes qui précèdent la dernière sont ce que l'on appelle le "traceback". C'est ce qui permet de trouver rapidement où s'est produite l'erreur. Ces lignes vont en général 2 par 2 :
File "test.py", line 3, in test
print table[4]
La première ligne indique :
- le fichier dans lequel on se trouve,
- la ligne de ce fichier à laquelle s'est produite l'erreur,
- le nom de la fonction que l'on est en train d'exécuter.
La seconde ligne présente l'instruction qui a fait planter le programme.
Les paires de lignes qui précèdent celle-ci sont les appels de fonctions successifs qui ont mené l'interpréteur jusqu'à cette instruction.
Cela permet de savoir grosso-modo où en était votre programme dans son exécution avant qu'il ne plante.
J'ai identifié mon exception, que faire ?
Ce paragraphe consignera, au fur et à mesure les exceptions rencontrées couramment, et référencera les posts contenant les explications des participants. Classés par exception et par message.
Afin de participer à ce topic, merci d'organiser vos posts de la manière suivante :
Titre : type de l'exception et message de Python
…suivi des explications sur :
- les cas où Python lève cette exception avec ce message, [obligatoire]
- que vérifier/corriger dans le code (en général), [obligatoire]
- quelques rappels sur les "bonnes pratiques" à adopter dans ces cas de figure. [optionnel]
Essayez de préciser la version de Python (si le comportement est différent entre 2.x et 3.x).
Pour reporter du code tapé dans le shell Python, utilisez de préférence la balise code avec l'attribut "pycon", adapté à l'affichage du shell Python :
Oui, ça peut servir de base. (Merci d'avoir répondu, j'vais pouvoir commencer à ajouter du contenu sans attendre 24h ).
Mon idée de base était surtout de partir des erreurs "j'ai pas compris mon exception" les plus courantes dans le forum, mais on peut aussi essayer d'anticiper, quoi qu'il est moins évident dans ce cas de trouver le genre d'inattention "typique de débutant" provoquant l'exception en question.
Concrètement : on vient d'aider un Zér0 à résoudre un problème en étant très tenté de lui dire « mais enfin ! Il suffit de LIRE l'erreur », on remarque que cette exception n'est pas encore consignée ici. Hop ! On la rajoute, et la prochaine fois, on n'a plus qu'à renvoyer vers le lien de l'explication en question, de manière à ce que la personne cherche aussi à comprendre son erreur (ce qui est plus formateur) plutôt qu'on la résolve pour lui.
Merci fred. Je vais rajouter un texte pour le message sur la constance tabulation/espace. Je rajoute le lien en premier post.
SyntaxError: inconsistent use of tabs and spaces in indentation
Quand cette exception se produit-elle ?
Comme vous le savez, les blocs en Python sont gérés par l'indentation. Pour indenter un bloc, il est possible d'utiliser des tabulations ou des espaces. Python accepte tout cela, mais exige que vous soyez consistants dans votre manière d'indenter vos blocs.
Cela signifie que si vous choisissez d'indenter un bloc avec des espaces, tous les blocs de votre programme devront être indentés avec des espaces, sans tabulation. Si vous ne le faites pas, Python n'a plus moyen de comprendre votre code, et retourne cette erreur.
Comment réparer cette erreur ?
Sur n'importe quel éditeur de texte digne de ce nom, il existe une fonction "Rechercher/Remplacer". Utilisez-la pour rechercher les tabulations, et les remplacer par 4 espaces.
Rappel sur les bonnes pratiques de Python
Comme le conseille Guido van Rossum dans la PEP-8, il est fortement conseillé de toujours indenter votre code en utilisant 4 espaces. Vous pouvez pour cela régler votre éditeur de texte préféré, de manière à ce qu'il insère des espaces en lieu et place des tabulations.
Autre détail : pour que votre code soit plus lisible, il est conseillé que vous utilisiez tout le temps le même nombre d'espaces quel que soit le niveau d'indentation.
>>> func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'func' is not defined
Pourquoi python lève-t-il cette exception ?
Cette exception signifie que la fonction func n'a pas été définie, peut-être vous êtes-vous trompé dans le nom de la fonction, ou avez oublié d'importer un module. Elle est valable pour l'appel à tout type d'objet (variable, classe, etc.)
>>> 50 / 0
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
50 / 0
ZeroDivisionError: int division or modulo by zero
Pourquoi Python lève t-il une exception?
Cette exception signifie que l'on essaye de diviser un nombre par zéro grâce à l'opérateur '/' (division) ou '%' (modulo). Or nous savons que cela est impossible.
Comment modifier son code en conséquence ?
Il faut s'efforcer à ne pas diviser un nombre par zéro, ou bien utiliser les blocs try/except:
try:
50 / 0
except ZeroDivisionError:
print("Vous ne pouvez pas diviser par zéro !")
Rappel sur les bonnes pratiques de Python
Vérifiez toujours de ne pas diviser un nombre par zéro, que se soit avec le modulo (%) ou le signe de la division (/).
Les blocs try/except sont vos amis; n'hésitez pas à vous en servir.
NameError: name 'X' is not defined
(en utilisant la fonction input sous Python 2)
Exemple de code problématique
Voici un exemple de code sous Python 2.x
>>> nom = input("comment t'appelles-tu ? ")
comment t'appelles-tu ? NoHaR
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'NoHaR' is not defined
D'où vient cette erreur ?
Vous avez probablement dû étudier la fonction input dans un tutoriel abordant Python 3, mais voulu l'utiliser en Python 2. Malheureusement, cette fonction se comporte différemment entre les deux versions.
En Python 2, la fonction input va chercher à interpréter la saisie de l'utilisateur, plutôt que de la restituer sous forme d'une chaîne de caractères. Par exemple :
>>> resultat = input("entrez un calcul à effectuer :")
entrez un calcul à effectuer : 3 * 4 + 12 * 15.0
>>> resultat
192.0
Ainsi, si l'utilisateur retourne autre-chose qu'une expression que Python peut interpréter, ce dernier l'enverra ballader, conformément à la signification générale d'une NameError.
Comment y remédier ?
Sous Python 2, pour obtenir le même comportement que la fonction input de Python 3, il faut utiliser la fonction raw_input :
>>> nom = raw_input("comment t'appelles-tu ? ")
comment t'appelles-tu ? NoHaR
>>> nom
'NoHaR'
(Mon message ne me paraît pas tout à fait clair, si vous avez des idées pour l'améliorer, je suis preneur)
Réponse à fred : oui, je me suis mal exprimé, je sautais la vérification et passais directement à l'ajout du répertoire s'il n'était pas présent.
Cela signifie que si vous choisissez d'indenter un bloc avec 4 espaces, tous les blocs de votre programme devront être indentés de la même manière, 4 espaces par 4 espaces. Si vous ne le faites pas, Python n'a plus moyen de comprendre votre code, et retourne cette erreur.
Je pense que tu t'es mal exprimé, mais il est possible d'indenter différement chaques blocs, avec n'importe quel nombre d'espaces ou de tabulations (et pas un mélange des deux).
Exemple :
def func(n):
if n > 0:
print("Le nombre", n, "est positif.")
elif n < 0:
print("Le nombre", n, "est négatif.")
else:
print("Le nombre", n, "est nulle.")
Sur n'importe quel éditeur de texte digne de ce nom, il existe une fonction "Rechercher/Remplacer". Utilisez-la pour rechercher les tabulations, et les remplacer par 4 espaces.
Sur n'importe quel éditeur de texte digne de ce nom (dérivés de scintilla, etc) tu peux paramétrer le style d'indentation en fonction de l'extension de fichier. Donc pour les *.py il faut penser à mettre la largeur des tabs à 4 espaces. D'autre part sur scite (et autres) tu fais "indentation settings" et tu peux passer des espaces aux tabulations sans avoir à rechercher-remplacer...
Pour revenir au topic, une erreur courante en python est de ne pas coder en python mais en C, par exemple oublier d'utiliser itertools, enumerate, zip(), mettre un while au lieu d'un for, etc.
Je connaissais ce fil que tu avais ouvert mais je n'avais pas vu qu'il contenait un passage en revue d'exceptions courantes.
Perso, je n'aurais pas parlé d'exception, terme à la fois
-- trop commun ; en effet, cela réfère à une situation exceptionnelle, on pense à des inondations, courts-circuits, tremblement de terre alors qu'en fait en programmation le sens est assez différent, je dirais même que pour un programmeur en train d'écrire son programme, lever des exceptions est tout le contraire d'exceptionnel.
-- trop spécifique puisque il se réfère à un mécanisme spécialisé des exceptions de Python auquel le débutant confronté à un message d'erreur n'est pas forcément initié.
Je pense que ton fil peut être utile aux débutants et qu'il permet de débloquer un certain nombre de situations.
Toutefois, je pense que cela concerne des situations assez élémentaires ; la réalité est souvent plus complexe, par exemple, un NameError peut être levé de façon sournoise, par exemple lorqu'on se réfère à une varibale toto qui est définie au cours d'une partie du flux d'exécution (une condition if par exemple) et qui n'est pas visitée à chaque exécution. Assez souvent, les exceptions sont juste un artefact et témoignent en fait d'un bug de conception, algorithmique ou un manque de maîtrise des différents éléments (syntaxe, sémantique) du langage de programmation. Donc, je pense qu'il faut informer le débutant qu'hélas, savoir interpréter une exception rapportée dans un message d'erreur n'est souvent que le début d'un jeu de piste et pas la fin de l'histoire.
Perso, je n'aurais pas parlé d'exception, terme à la fois
OK. Dès que j'ai le temps je reformulerai le post originel, pour parler d'erreurs et de messages d'erreurs, tout en introduisant le terme "exception".
Citation : candide
Toutefois, je pense que cela concerne des situations assez élémentaires
Tu n'as pas tort. Ceci dit, je vois mal comment on pourrait s'attaquer à ce problème particulier, à moins de rédiger dans un post (référencé en premier post) une sorte de micro-tuto sur le sujet, accolé à la liste d'erreurs courantes, peut-être…
Si tu as des idées sur le sujet, elles sont les bienvenues.
Toutefois, je pense que cela concerne des situations assez élémentaires
Tu n'as pas tort. Ceci dit, je vois mal comment on pourrait s'attaquer à ce problème particulier, à moins de rédiger dans un post (référencé en premier post) une sorte de micro-tuto sur le sujet, accolé à la liste d'erreurs courantes, peut-être…
Il faut surtout complètement repenser la conception des documents d'apprentissage des langages de programmation, vaste question ...
Citation : nohar
Si tu as des idées sur le sujet, elles sont les bienvenues.
Ce ne sont pas les idées qui me manquent mais plutôt le temps pour les mettre en oeuvre (bon, on en est tous là je suppose ). La question de l'erreur touche à des domaines très différents. Les modes et les contenus d'apprentissage sont en partie responsables du fait que nous fassions des erreurs de programmation, comme je l'ai dit plus haut, les documents d'apprentissage ayant tendance à passer sous silence cette fraction très importante du temps où nous mettons au point notre code à corriger des erreurs pour nous présenter que la partie achevée.
Pour en revenir à la question de la chaîne d'erreur, un document d'apprentissage d'un débogueur en ligne ne serait-il pas une aide à ceux qui qui voudraient corriger plus facilement leurs programmes ? Au-delà de ça, un débogueur est un outil très utile pour chasser les erreurs bien sûr mais plus généralement pour apprendre à programmer.
L'interprétation d'un tel code retourne une erreur comme celle-ci :
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'Fichier_quelconque.txt'
D'où vient cette erreur ?
Dans l'exemple ci-dessus, le fichier "Fichier_quelconque.txt" ne se trouve pas dans le répertoire courant de l'interpréteur python (ou bien il n'existe tout simplement pas !). Il ne peut donc pas y accéder, et renvoie donc cette erreur...
Comment y remédier ?
Il suffit d'indiquer le chemin correct du fichier que l'on veut ouvrir.
Pour cela, on peut changer de répertoire courant avec la fonction chdir() du module os :
from os import chdir
chdir("/Chemin/du/répertoire/du/fichier")
monfichier=open("Fichier_quelconque.txt","r")
contenu=monfichier.read()
monfichier.close()
print(contenu)
Si l'on veut s'épargner une importation, on peut également renseigner directement le chemin correct du fichier dans la fonction open, qu'il soit relatif ou absolu :
La méthode la plus sûre et la plus efficace pour l'ouverture d'un fichier reste celle qui emploie les blocs tryexcept ainsi que le mot clé with, de cette manière :
try:
with open("Fichier_quelconque.txt") as f:
print f.readlines()
except IOError:
print ("Erreur !")
Je tiens tout d'abord à vous remercier infiniment pour ce tutoriel.
J'ai juste un petit souci avec mon IDLE. Lorsque je lance un programme en cliquant touche droite => edit with IDLE un message d'erreur s'affiche comme indiqué ci-après.
Pourriez-vous m’éclaircir cela car je n'y arrive pas à exécuter mes petits programmes.
Cela signifie que tu as essayé d'utiliser un nombre comme un tableau:
>>>a = 453215869514
>>>a[5]=7 #lève effectivement une erreur de ce type
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object does not support item assignment
voilà avec un tableau:
>>>a = ['a','z','e','r','t','y','u','i','o','p']
>>>a[5]="v" #ne lève plus d'erreur de ce type
['a','z','e','r','t','y','v','i','o','p']
d'autres types de données peuvent également être utilisé comme un tableau, avec cependant quelques réstrictions:
les tuples et les chaînes de caractères.(ils ne peuvent être modifiés après initialisation)
>>>tup=('a','b') #tuple
>>>tup[0]
'a'
>>>tup[1]
'b'
>>>tup[1]='c'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>chaine='ab' #chaine
>>>chaine[0]
'a'
>>>chaine[1]
'b'
>>>chaine[1]='c'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
- Edité par pythan 31 janvier 2014 à 11:27:42
Bevet Breizh! Breizh dizalc'h! Betek an trec'h! Ha mallozh ruz d'ar c'hallaoued! Trouvez votre voie
@pythan : euh... fais gaffe, les chaines de caractères ne sont pas mutables, essayer de changer un caractère via a[5]='v' lève une exception du style "TypeError: 'str' object does not support item assignment"...
entwanne — @entwanne — Un zeste de Python — La POO en Python — Notions de Python avancées — Les secrets d'un code pythonique
entwanne — @entwanne — Un zeste de Python — La POO en Python — Notions de Python avancées — Les secrets d'un code pythonique
Et voilà !