Une exception en Python : l'erreur qui interrompt le programme

Une exception est le signal qu'un programme émet quand il ne peut plus continuer. Elle interrompt l'exécution et remonte jusqu'à qui saura la traiter.
5 min de lecture
Believemy logo

Définition

Un programme rencontre sans arrêt des situations qu'il ne sait pas résoudre : le fichier attendu n'est pas là, un calcul demande une division par zéro, une clé réclamée manque au dictionnaire. Continuer coûte que coûte produit des résultats faux, découverts des heures plus tard ; s'arrêter en disant ce qui cloche laisse une chance de comprendre.

Python a choisi la seconde, et ce choix porte un nom : l'exception. C'est le signal qu'émet un programme lorsqu'il refuse de poursuivre. Elle interrompt l'instruction en cours, puis remonte la pile des appels à la recherche d'un endroit prévu pour la traiter. Sans personne pour la prendre, elle sort du programme et affiche un traceback.

Cette remontée est précieuse : l'endroit où le problème survient et celui où on le rattrape n'ont pas besoin d'être voisins. Une fonction enfouie très profond signale l'incident, et le code appelant s'en charge, seul à savoir quoi en faire.

Le rattrapage, lui, demande de délimiter la zone à surveiller puis d'annoncer quoi faire si elle échoue.

PYTHON
try:
    # si nombre_de_parts vaut 0, cette ligne lève ZeroDivisionError
    total = montant / nombre_de_parts
except ZeroDivisionError:
    # on n'arrive ici que si l'exception annoncée a bien été levée
    total = 0

Le mot est mal choisi en français : une exception n'a rien d'exceptionnel. C'est le mode normal de signalement d'un problème, et un programme solide en lève beaucoup, volontairement.


Une famille, pas une liste

Python définit plusieurs dizaines de types d'exceptions. Les énumérer un par un dans chaque bloc except serait interminable, et en oublier un suffirait à laisser le programme s'arrêter.

D'où le choix de les ranger en arbre, par héritage : chaque type descend d'un type plus général, et intercepter un parent attrape du même coup toutes ses filles. Vous réglez ainsi la largeur du filet.

L'arbre des exceptions et ce qu'un bloc except attrape Exception en haut ; en dessous ValueError, TypeError et LookupError ; sous LookupError, IndexError et KeyError. Le cadre orange montre que except LookupError attrape trois types, quand except KeyError n'en attrape qu'un. Exception ValueError TypeError LookupError IndexError KeyError except LookupError attrape ces 3 types except KeyError attrape 1 seul type

Une poignée de types revient en permanence. Voici ce que chacun signale, la question à se poser quand un nom inconnu apparaît dans un traceback.

ExceptionCe qu'elle signale
TypeErrorUne opération sur un type qui ne la supporte pas
ValueErrorLe bon type, mais une valeur inacceptable
KeyErrorUne clé absente d'un dictionnaire
IndexErrorUn rang au-delà de la fin d'une séquence
AttributeErrorUn attribut que l'objet ne possède pas
LookupErrorLe parent de KeyError et IndexError

Au sommet de l'arbre se trouve Exception, l'ancêtre commun de presque tout : except Exception attrape donc à peu près n'importe quoi.

Attention

Un except Exception posé trop large avale aussi vos fautes de frappe : un nom de variable mal orthographié devient indiscernable d'une panne réseau, et le message affiché désigne la mauvaise cause.


Les lever soi-même

Les exceptions ne sont pas réservées au langage. Une fonction que vous écrivez reçoit elle aussi des demandes impossibles à honorer, retirer 500 euros d'un compte qui en contient 30, et sa façon de le signaler décide de la suite.

raise lève une exception depuis votre propre code, exactement comme Python le fait depuis le sien.

PYTHON
def retirer(solde, montant):
    if montant > solde:
        # on refuse avant de calculer : aucun montant faux ne sortira d'ici
        raise ValueError("Montant supérieur au solde")
    return solde - montant

Renvoyer une valeur de repli, None ou -1, paraît plus doux, et c'est là le problème : la vérification revient à l'appelant, qui l'oubliera un jour de fatigue. La valeur fausse circule alors dans le programme et n'éclatera que bien plus loin, sans rapport visible avec sa cause.

Une exception, elle, ne s'ignore pas par distraction : soit l'appelant s'en occupe, soit le programme s'arrête sur la ligne fautive, là où le problème est né.


Demander pardon plutôt que la permission

Reste une question de méthode : vaut-il mieux vérifier que tout est en ordre avant d'agir, ou agir puis rattraper l'échec ? Python assume la seconde voie.

Vérifier qu'un fichier existe avant de l'ouvrir semble plus propre, mais laisse une fenêtre pendant laquelle il peut être supprimé ou verrouillé. L'ouvrir directement dans un try referme cette fenêtre : la tentative et son échec se traitent d'un seul geste.

Le chemin normal se lit alors d'une traite, et un bloc finally garantit le nettoyage, qu'une exception ait été levée ou non.

Bon à savoir

Cette préférence a un nom dans la communauté Python : EAFP, pour « easier to ask forgiveness than permission ». L'approche inverse s'appelle LBYL, « look before you leap ».


Questions fréquentes

Question

Quelle différence entre une exception et une erreur de syntaxe ?

Le moment de la détection les sépare. Une SyntaxError est repérée pendant que Python lit le fichier : rien ne démarre, et aucun bloc d'interception ne peut la rattraper. Une exception survient pendant l'exécution, dans un code valide qui rencontre une situation qu'il ne sait pas traiter.

Question

Faut-il intercepter toutes les exceptions ?

Non, seulement celles que vous savez traiter. Une exception qui remonte jusqu'en haut signale au moins un vrai problème. Avalée par un except vide, elle donne un programme qui continue sur des données incohérentes, ce qui coûte bien plus cher qu'un arrêt franc.

Question

Une exception ralentit-elle le programme ?

Son coût n'apparaît qu'au moment où elle est levée, jamais sur le chemin normal : un bloc try qui ne déclenche rien ne coûte presque rien, ce qui rend cette écriture adaptée aux boucles. Savoir quoi rattraper et quoi laisser passer relève en revanche du jugement, et notre formation Python travaille la question sur des cas concrets.

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.