.. _tut-exceptions:
**************
Les exceptions
**************
Une vidéo de présentation des exceptions...
.. raw:: html
Comme d'autres langages, Python dispose d'un mécanisme de gestion des exceptions. Une exception est une erreur qui se produit lors de l'exécution du programme. Gérer une exception c'est interrompre le cours normal du programme pour déclencher un traitement particulier permettant de poursuivre, ou pas, son exécution.
Quelques exceptions courantes
=============================
Il existe un très grand nombre `d'exceptions `_ organisées de façon structurée et hiérarchique. Voici les plus courantes.
Les erreurs de rédaction
------------------------
Python n'étant pas un langage compilé, les erreurs dans la rédaction du code ne sont détectées que pendant la phase d'exécution et déclenchent des exceptions.
Ainsi les erreurs de syntaxe sont elles même des exceptions de type :py:exc:`SyntaxError`::
>>> if x%2 == 0 print('x est pair')
File "", line 1
if x%2 == 0 print('x est pair')
^
SyntaxError: invalid syntax
Une exception de type :py:exc:`IndentationError` se produit lorsque l'on indente mal son programme::
>>> if True:
... print('ligne indentée avec une tabulation')
... print('ligne indentée avec 4 espaces')
File "", line 3
print('4 espaces')
^
IndentationError: unindent does not match any outer indentation level
Pour éviter ce type d'erreur, on peut paramétrer son éditeur de texte :
- en visualisant les caractères non imprimables ;
- en remplaçant le caractère ``tab`` par 4 espaces.
Les erreurs de conception
-------------------------
Même si la syntaxe est correcte, d'autres problèmes de conception peuvent survenir.
L'appel à une référence qui n'existe pas est une exception de type :py:exc:`NameError`::
>>> if x%2 == 0: print('x est pair')
...
Traceback (most recent call last):
File "", line 1, in
NameError: name 'x' is not defined
Il peut également se produire un problème lors de l'importation d'un module::
>>> import un_module_qui_nexiste_pas
Traceback (most recent call last):
File "", line 1, in
ImportError: No module named 'un_module_qui_nexiste_pas'
ou lorsqu'on applique une fonction a un objet inapproprié::
>>> len(True)
Traceback (most recent call last):
File "", line 1, in
TypeError: object of type 'bool' has no len()
Les exceptions numériques
-------------------------
Au cours de l'exécution d'un programme syntaxiquement correct et bien conçu (en ce sens qu'il ne déclenche pas les exceptions précédentes), il peut arriver qu'une opération conduise à une erreur. C'est par exemple le cas lorsqu'un calcul numérique potentiellement "dangereux" utilise un résultat non connu à l'avance::
>>> res = 0
>>> 1/res
Traceback (most recent call last):
File "", line 1, in
ZeroDivisionError: division by zero
ou dépassant les capacités de la machine::
>>> 2.0**4000
Traceback (most recent call last):
File "", line 1, in
OverflowError: (34, 'Result too large')
L'accès à des ressources externes
---------------------------------
Lorsque le programme tente d'accéder à une ressource qui n'existe pas ou qui n'est pas disponible au moment de l'exécution, une exception de type :py:exc:`FileNotFoundError` est déclenchée::
>>> open('un_fichier_qui_nexiste_pas.txt', 'r')
Traceback (most recent call last):
File "", line 1, in
FileNotFoundError: [Errno 2] No such file or directory: 'un_fichier_qui_nexiste_pas.txt'
La gestion des exceptions
=========================
Après ce tour d'horizon des exceptions les plus courantes, voici le moyen de les gérer.
La philosophie de Python n'est pas de contrôler en amont les conditions d'exécution du programme, ce qui peut être lourd, fastidieux et peu lisible mais de tenter d'exécuter l'opération en apportant une solution de repli au cas où elle ne fonctionnerait pas. Ceci s'effectue avec la construction :keyword:`try` - :keyword:`except` - :keyword:`else` - :keyword:`finally`, similaire dans le principe au ``try-catch-finally`` de Java :
#. on tente d'exécuter les instructions contenues dans le bloc :keyword:`try` ;
#. si aucune erreur n'est produite, on exécute ensuite les instructions contenues dans les blocs :keyword:`else` puis :keyword:`finally` ;
#. sinon, on exécute les instructions contenues dans les blocs :keyword:`except` puis :keyword:`finally`;
.. admonition:: A expérimenter...
On considère le code suivant :
.. code-block:: python
:linenos:
for res in range(3):
print("Trying to compute 1 /",res)
try:
print(" 1/",res, "=", 1/res)
except ZeroDivisionError:
print(" 1 /",res,"tend vers l'infini")
else:
print(" Le résultat a une valeur finie")
finally:
print(' Fin de la division')
Conjecturer le résultat de l'exécution. Vérifier dans un interpréteur interactif.
.. admonition:: A expérimenter...
On considère maintenant le code suivant :
.. code-block:: python
:linenos:
for file in ["existing_file.txt", "non_existing_file.txt"]:
try:
print("Trying to open : ***", file, "***")
f = open(file, 'r')
print(" le fichier a été trouvé")
print(" nombre de caractères :",len(f.read()))
except FileNotFoundError:
print(" le fichier n'a pas été trouvé")
finally:
f.close()
print(" Fin de l'opération")
Conjecturer le résultat de l'exécution. Vérifier dans un interpréteur interactif.
C'est une bonne pratique que d'encapsuler les opérations non sûres dans des blocs :keyword:`try` - :keyword:`except` - :keyword:`else` - :keyword:`finally`.
EAFP vs LBYL
============
On peut également utiliser les exceptions pour simplifier le flux de traitement. Dans certains langages de programmation (C par exemple), on utilise le paradigme Look Before You Leap (LBYL) qui consiste à effectuer l'ensemble des tests nécessaires avant de déclencher une opération. Les langages comportant une gestion des exceptions peuvent mettre en oeuvre le paradigme Easier to Ask for Forgiveness than Permission (EAFP) dont le principe de base est de tenter l'opération et de réagir si elle échoue.
LBYL
----
Si on met en oeuvre le paradigme LBYL, on sécurise l'accès à une clé de dictionnaire avec le code suivant::
if "key" in my_dict:
x = my_dict["key"]
else:
handle_missing_key()
En cas de programmation concurrente (multithreads), on peut donc aboutir à `une situation de compétition `_, conduisant un comportement non prédictible. Dans l'exemple qui précède, le dictionnaire peut être modifié entre le test et l'accès à la clé.
EAFP
----
Pour le paradigme EAFP::
try:
x = my_dict["key"]
except KeyError:
handle_missing_key()
Lorsque l'erreur est peu probable, l'utilisation du paradigme EAFP conduit à de meilleures performances algorithmiques.
Les exceptions sont des objets
==============================
En Python tout est objet, les exceptions ne font pas exception (!) à la règle.
On considère le code suivant::
>>> try:
... a = 1/0
... except Exception as e:
... print(type(e))
... print(dir(e))
...
[... 'args', 'with_traceback']
Ici la référence ``e`` est affectée à l'exception générée, ce qui permet de la manipuler ensuite.
Ce qu'il faut retenir
=====================
.. quiz:: quizz-11
:title: Les exceptions
- :quiz:`{"type":"TF","answer":"F"}` En Python, une exception confirme la règle
- :quiz:`{"type":"TF","answer":"F"}` Une exception interrompt systématiquement l'exécution d'un programme
- :quiz:`{"type":"TF","answer":"T"}` Une exception non gérée interrompt systématiquement l'exécution d'un programme
- :quiz:`{"type":"TF","answer":"T"}` On peut intercepter un problème d'exécution avant qu'il n'interrompe le programme
- :quiz:`{"type":"TF","answer":"F"}` Il est obligatoire d'intercepter un problème d'exécution avant qu'il n'interrompe le programme
- :quiz:`{"type":"TF","answer":"T"}` Il est souhaitable d'intercepter un problème d'exécution avant qu'il n'interrompe le programme
- :quiz:`{"type":"TF","answer":"T"}` Les exceptions sont des objets
- L'acronyme LBYL signifie :quiz:`{"type":"FB","answer":"Look Before You Leap", "size":18,"flags":"fuzzy"}`
- L'acronyme EAFP signifie :quiz:`{"type":"FB","answer":"Easier to Ask for Forgiveness than Permission", "size":18,"flags":"fuzzy"}`
- :quiz:`{"type":"TF","answer":"T"}` Une exception est déclenchée quand une erreur se produit dans le bloc ``try``
- :quiz:`{"type":"TF","answer":"F"}` Une exception est déclenchée quand une erreur se produit dans le bloc ``except``
- :quiz:`{"type":"TF","answer":"F"}` Lorsqu'il n'y a pas d'erreur dans le bloc ``try``, le bloc ``except`` est exécuté
- :quiz:`{"type":"TF","answer":"T"}` Lorsqu'il n'y a pas d'erreur dans le bloc ``try``, le bloc ``else`` est exécuté
- :quiz:`{"type":"TF","answer":"T"}` Lorsqu'il n'y a pas d'erreur dans le bloc ``try``, le bloc ``finally`` est exécuté
- :quiz:`{"type":"TF","answer":"T"}` Lorsqu'il y a une erreur dans le bloc ``try``, le bloc ``except`` est exécuté
- :quiz:`{"type":"TF","answer":"F"}` Lorsqu'il y a une erreur dans le bloc ``try``, le bloc ``else`` est exécuté
- :quiz:`{"type":"TF","answer":"T"}` Lorsqu'il y a une erreur dans le bloc ``try``, le bloc ``finally`` est exécuté
- :quiz:`{"type":"TF","answer":"T"}` Une erreur de syntaxe déclenche une exception
- :quiz:`{"type":"TF","answer":"T"}` L'accès à une référence inconnue déclenche une exception
- :quiz:`{"type":"TF","answer":"T"}` L'accès à un fichier introuvable déclenche une exception