, ,

Python: les exceptions

Lorsque l'exception est levée, elle donne la raison de l'erreur et elle permet également de situer cette erreur dans le contexte d’exécution du programme. Elle conduit à l'interruption du programme si elle n'est pas explicitement capturée.

On capture les exception via le bloc try…except. Toutes les instructions présentes dans le bloc try sont évaluées et les exceptions produites vont être comparées à celles gérées dans le bloc except. Dans le bloc du except, on précise l'exception que l'on souhaite prendre en compte et le code qui doit s'exécuter lorsque l'exception se produit.

On capture l'exception, on produit un comportement approprié réagissant à cette exception et on continue l’exécution normale du programme.

On peut ajouter autant de close except que nécessaire pour réagir aux exceptions pouvant être levées par un seul bloc try.

En Python on peut définir une close excpet sans préciser aucun type d' exception. C'est dans la majorité des cas une très mauvaise pratique car la close va masquer (en capturant) toutes les exceptions produites par le code sans y réagir spécifiquement.

De manière générale on capture toujours les exceptions que l'on a étudié et prévu dans notre code et on laisse remonter les exceptions non prévues.

Une caractéristique importante des exceptions est qu'elles “bubble”: elles remontent la pile d’exécution jusqu’à arrêter le programme (c'est le mécanisme de bubbling). La pile d’exécution contient la liste ordonnée des fonctions qui ont été appelées par le programme mais qui n'ont pas encore retourné de résultat et donc qui restent en cours d’exécution. L'exception lorsqu'elle se produit au sein d'une fonction arrête son exécution et remonte la pile d’exécution jusqu’à ce qu'elle soit capturée ou si elle atteint le haut de la pile arrête l’exécution du programme.

Ce mécanisme de bubbling à deux avantages majeurs:

Une bonne pratique est de capturer les exceptions bien identifiées au plus près de l'endroit où elles se produisent dans le code afin d'avoir les réactions les plus appropriées à l'origine du problème.

Pour connaître la liste des exceptions pouvant être produites, il faut lire la documentation des modules et objets que l'on utilise.

try
   # Bloc d'instructions pouvant produire
   # une ou plusieurs exceptions
   ...
   ...
except TypeExceptionA:
   # Instructions spécifiques de réaction
   # à l' exception A
   ...
   ...
except TypeExceptionB:
   # Instructions spécifiques de réaction
   # à l' exception B
   ...
   ...
else:
   # Bloc d’instructions exécuté si aucune
   # exception n'est produite
   ...
   ...
finally:
   # Bloc d'instruction exécuté quoi qu'il arrive
   ...
   ...
Le bloc finally est intéressant: il permet de définir un bloc pouvant être exécuté par une une fonction juste après le return.
def return_with_finally(number):
    try:
        # le return est dans le bloc try
        # meme si aucune exception n'est produite pas l'instruction
        # le bloc finally sera exécuté après le return
        return 1/number
 
    except ZeroDivisionError as e:
        print(f"OOPS, {type(e)}, {e}")
        return("zero-divide")
 
    finally:
        print("on passe ici même si on a vu un return")

L'utilisation du bloc else est préférable à l'ajout d'instructions supplémentaires dans le bloc try. Cela empêche la capture accidentelle d’exceptions levées par le les instructions du bloc else qui ne seront pas interceptées par le bloc try..except courant.

Le langage Python intègre une hiérarchie d'exceptions directement exploitable.

Références