
La gestion des exceptions en Python permet de traiter les erreurs sans que le programme ne s'arrête brutalement. Le principe : le code risqué va dans un bloc try, la réaction à l'erreur dans un bloc except. Ce guide couvre tout le mécanisme : capturer une ou plusieurs exceptions, les blocs else et finally, lever une erreur avec raise, créer ses exceptions personnalisées et adopter les bons réflexes en production (journaliser, chaîner, ne pas masquer).
Il s'adresse aux débutants qui veulent des scripts robustes et à celles et ceux qui automatisent des tâches où une erreur non gérée casse tout un traitement. Chaque notion est illustrée par un exemple exécutable. Le code a été testé avec Python 3.12.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Capturer une ou plusieurs erreurs avec
try/except. - Utiliser
elseetfinallypour le succès et le nettoyage. - Lever vos propres erreurs avec
raise, et les chaîner avecraise ... from. - Créer des exceptions personnalisées adaptées à votre domaine.
- Journaliser les erreurs proprement plutôt que de les masquer.
Pourquoi gérer les exceptions ?
Section intitulée « Pourquoi gérer les exceptions ? »Gérer les exceptions permet d'anticiper les erreurs potentielles et d'éviter que le programme ne se termine brutalement. Sans gestion des exceptions, une erreur non traitée provoque l'arrêt immédiat du programme, souvent avec un message d'erreur peu compréhensible pour l'utilisateur final. En capturant et en traitant les exceptions, il est possible de :
- Prévenir les plantages du programme.
- Fournir des messages d'erreur clairs et utiles.
- Libérer proprement les ressources (fichiers, connexions réseau, etc.).
- Maintenir le flux normal du programme en cas d'erreurs mineures.
Comprendre les types d'exceptions
Section intitulée « Comprendre les types d'exceptions »Python dispose de nombreux types d'exceptions intégrées, chacune correspondant à une erreur spécifique. Voici quelques-unes des plus courantes :
- ZeroDivisionError : Tentative de division par zéro.
- TypeError : Opération sur un objet d'un type inapproprié.
- ValueError : Valeur ayant le bon type mais une valeur inappropriée.
- IndexError : Indice de séquence hors de portée.
- KeyError : Clé non trouvée dans un dictionnaire.
- AttributeError : Tentative d'accès à un attribut inexistant d'un objet.
- ImportError : Échec d'importation d'un module.
Exemples :
# ZeroDivisionErrorprint(10 / 0) # Provoque une ZeroDivisionError
# TypeErrorprint('5' + 3) # Provoque une TypeError
# ValueErrorint('abc') # Provoque une ValueError
# IndexErrorliste = [1, 2, 3]print(liste[5]) # Provoque une IndexError
# KeyErrordictionnaire = {'a': 1, 'b': 2}print(dictionnaire['c']) # Provoque une KeyErrorComprendre ces exceptions permet de les anticiper et de les gérer correctement dans le code.
Utiliser try et except
Section intitulée « Utiliser try et except »Le bloc try...except permet de capturer les exceptions et de définir une
réponse appropriée. Le code susceptible de provoquer une exception est placé
dans le bloc try et le traitement de l'erreur dans le bloc except.
Syntaxe de base :
try: ... # Code susceptible de provoquer une exceptionexcept NomDeLException: ... # Code à exécuter en cas d'exceptionExemple :
try: num = int(input("Entrez un nombre : ")) resultat = 10 / num print(f"Résultat : {resultat}")except ZeroDivisionError: print("Erreur : division par zéro.")except ValueError: print("Erreur : veuillez entrer un nombre valide.")Dans cet exemple :
- Si l'utilisateur entre zéro, une ZeroDivisionError est levée et gérée.
- Si l'utilisateur entre une valeur non numérique, une ValueError est levée et gérée.
- Dans les deux cas, le programme affiche un message d'erreur clair au lieu de se terminer brutalement.
Capturer plusieurs exceptions
Section intitulée « Capturer plusieurs exceptions »Il est possible de gérer plusieurs exceptions dans un même bloc except en
les regroupant dans un tuple. Cela permet de traiter de la même manière
plusieurs types d'exceptions.
Exemple :
try: num = int(input("Entrez un nombre : ")) resultat = 10 / numexcept (ZeroDivisionError, ValueError) as e: print(f"Erreur : {e}")Ici, si une ZeroDivisionError ou une ValueError est levée, le même bloc
except les gère et le message d'erreur associé est affiché.
Utiliser else avec try...except
Section intitulée « Utiliser else avec try...except »Le bloc else s'exécute si aucune exception n'est levée dans le bloc try.
Il est utile pour exécuter du code qui doit seulement s'exécuter si tout s'est
bien passé dans le try.
Exemple :
try: num = int(input("Entrez un nombre : "))except ValueError: print("Erreur : veuillez entrer un nombre valide.")else: print(f"Vous avez entré le nombre {num}.")Dans cet exemple, le message de confirmation n'est affiché que si la conversion en entier s'est déroulée sans erreur.
Le bloc finally
Section intitulée « Le bloc finally »Le bloc finally est exécuté qu'une exception ait été levée ou non. Il est
utile pour effectuer des opérations de nettoyage ou libérer des ressources,
comme fermer un fichier ou une connexion.
Exemple :
try: num = int(input("Entrez un nombre : ")) resultat = 10 / numexcept ZeroDivisionError: print("Erreur : division par zéro.")except ValueError: print("Erreur : veuillez entrer un nombre valide.")finally: print("Fin de l'opération.")Ici, le message "Fin de l'opération." est toujours affiché, que le programme ait rencontré une exception ou non.
Lever des exceptions avec raise
Section intitulée « Lever des exceptions avec raise »Il est possible de lever explicitement une exception en utilisant le mot-clé
raise. Cela permet de signaler qu'une condition anormale s'est produite et de
transférer le contrôle au bloc except approprié.
Exemple :
def verifier_age(age): if age < 0: raise ValueError("L'âge ne peut pas être négatif.") elif age < 18: print("Vous êtes mineur.") else: print("Vous êtes majeur.")
try: verifier_age(-5)except ValueError as e: print(f"Erreur : {e}")Dans cet exemple, la fonction verifier_age lève une ValueError si l'âge
est négatif et le bloc try...except capture cette exception pour afficher un
message d'erreur.
Créer ses propres exceptions
Section intitulée « Créer ses propres exceptions »Il est possible de définir des exceptions personnalisées en créant des
classes qui héritent de la classe Exception. Cela permet de créer des erreurs
spécifiques à l'application ou au domaine.
Exemple :
class TemperatureError(Exception): """Exception levée lorsque la température est invalide.""" pass
def verifier_temperature(temp): if temp < -273.15: raise TemperatureError("La température ne peut pas être inférieure au zéro absolu.")
try: verifier_temperature(-300)except TemperatureError as e: print(f"Erreur : {e}")Dans cet exemple, une exception personnalisée TemperatureError est créée pour
gérer des erreurs liées à la température.
Les assertions
Section intitulée « Les assertions »Les assertions permettent de vérifier qu'une condition est vraie pendant l'exécution du programme. Si la condition est fausse, une AssertionError est levée. Elles sont principalement utilisées pendant le développement pour détecter des erreurs de logique.
Exemple :
def calculer_factorielle(n): assert n >= 0, "Le nombre doit être positif ou nul." if n == 0: return 1 else: return n * calculer_factorielle(n - 1)
print(calculer_factorielle(5)) # Affiche 120print(calculer_factorielle(-3)) # Provoque une AssertionErrorAttention : les assertions peuvent être désactivées lors de l'exécution du
programme avec l'option -O, il ne faut donc pas les utiliser pour la gestion
des erreurs en production.
Gestion des exceptions imbriquées
Section intitulée « Gestion des exceptions imbriquées »Il est possible d'imbriquer des blocs try...except pour gérer des exceptions à
différents niveaux.
Exemple :
try: try: num = int(input("Entrez un nombre : ")) resultat = 10 / num except ZeroDivisionError: print("Erreur : division par zéro.") else: print(f"Résultat : {resultat}")except ValueError: print("Erreur : veuillez entrer un nombre valide.")Exercice
Section intitulée « Exercice »Cet exercice vous aidera à comprendre comment utiliser les exceptions en Python pour gérer des erreurs qui pourraient survenir lors de l'exécution d'un programme. L'idée est de prévoir ces erreurs et d'agir en conséquence pour éviter que le programme ne plante.
Vous devez écrire une fonction qui permet de diviser deux nombres et de
gérer les erreurs possibles lors de cette division. Le but est de comprendre
comment utiliser le bloc try/except pour attraper des exceptions courantes,
comme la division par zéro ou une entrée non valide.
Détails de la fonction :
- La fonction s'appellera
diviser. - Elle prendra deux arguments :
numérateuretdénominateur. - La fonction doit gérer les erreurs suivantes :
- Division par zéro : Si le dénominateur est égal à 0, la fonction doit afficher un message d'erreur approprié : "Erreur : Division par zéro non permise."
- Entrée non numérique : Si l'un des deux arguments n'est pas un nombre (par exemple une chaîne de caractères), la fonction doit afficher un message d'erreur : "Erreur : Les deux valeurs doivent être des nombres."
Voici comment vous pourriez structurer votre fonction :
def diviser(numerateur, denominateur): try: # Effectuer la division resultat = numerateur / denominateur except ZeroDivisionError: # Gérer l'erreur de division par zéro print("Erreur : Division par zéro non permise.") except TypeError: # Gérer l'erreur de type non numérique print("Erreur : Les deux valeurs doivent être des nombres.") else: # Si tout se passe bien, afficher le résultat print(f"Le résultat de la division est : {resultat}")Cas de test :
- Cas normal (division valide) :
diviser(10, 2)Sortie attendue :
Le résultat de la division est : 5.0- Cas de division par zéro :
diviser(10, 0)Sortie attendue :
Erreur : Division par zéro non permise.- Cas avec des valeurs non numériques :
diviser(10, "deux")Sortie attendue :
Erreur : Les deux valeurs doivent être des nombres.- Cas où le numérateur est une chaîne de caractères :
diviser("dix", 2)Sortie attendue :
Erreur : Les deux valeurs doivent être des nombres.Bonnes pratiques en gestion d'exceptions
Section intitulée « Bonnes pratiques en gestion d'exceptions »- Spécifier les exceptions : Capturer des exceptions spécifiques permet de
mieux comprendre et gérer les erreurs. Éviter d'utiliser un
exceptsans préciser le type d'exception. - Fournir des messages clairs : Les messages d'erreur doivent être compréhensibles pour l'utilisateur, indiquant ce qui s'est mal passé et, si possible, comment y remédier.
- Ne pas masquer les erreurs : Éviter de capturer toutes les exceptions sans traitement approprié, car cela peut rendre le débogage difficile.
- Utiliser le bloc finally pour le nettoyage : S'assurer que les ressources sont correctement libérées, même en cas d'erreur.
- Lever des exceptions au lieu de retourner des codes d'erreur : Cela rend le code plus clair et facilite la gestion des erreurs.
- Ne pas abuser des exceptions : Les exceptions doivent être utilisées pour des conditions exceptionnelles, pas pour le contrôle de flux normal du programme.
Exemple de mauvaise pratique :
try: traiter_donnees()except Exception: pass # Mauvaise pratique, toutes les exceptions sont masquéesExemple de bonne pratique :
try: traiter_donnees()except FileNotFoundError as e: # Traitement approprié de l'exception print(f"Erreur : {e}")Depuis Python 3.11 : notes et groupes d'exceptions
Section intitulée « Depuis Python 3.11 : notes et groupes d'exceptions »Python 3.11 a modernisé les exceptions sur trois points utiles à connaître.
Le plus simple et le plus quotidien : add_note() (PEP 678) attache un contexte à une exception existante sans la remplacer. La note apparaît ensuite dans la trace :
try: age = int(valeur)except ValueError as e: e.add_note("Champ 'age' du fichier config.json") raisePour les programmes concurrents (asyncio, traitements parallèles), les groupes d'exceptions (PEP 654) permettent de lever plusieurs erreurs à la fois et de les trier avec la syntaxe except* :
try: raise ExceptionGroup("plusieurs échecs", [ ValueError("valeur invalide"), KeyError("clé absente"), ])except* ValueError as groupe: print("valeurs :", [str(e) for e in groupe.exceptions])except* KeyError as groupe: print("clés :", [str(e) for e in groupe.exceptions])Enfin, les messages d'erreur sont devenus plus précis : la trace souligne l'expression exacte qui a échoué (PEP 657), pas seulement le numéro de ligne, ce qui accélère le débogage sans rien changer à votre code.
En production : journaliser et chaîner
Section intitulée « En production : journaliser et chaîner »Dans un script d'exemple, print() suffit pour afficher une erreur. Dans un vrai programme, il faut journaliser : le module logging enregistre l'erreur avec sa pile d'appels complète, horodatée, sans polluer la sortie standard. La fonction logging.exception() est faite pour ça, à appeler dans un bloc except :
import logging
try: resultat = 10 / diviseurexcept ZeroDivisionError: logging.exception("Échec du calcul") # message + traceback completQuand vous relancez une erreur sous une forme plus parlante, chaînez avec raise ... from : la cause d'origine est conservée, ce qui rend le débogage bien plus simple.
def charger_config(chemin): try: with open(chemin, encoding="utf-8") as f: return f.read() except FileNotFoundError as e: raise RuntimeError(f"Configuration illisible : {chemin}") from eEnfin, quand une erreur précise est attendue et sans conséquence, contextlib.suppress exprime l'intention plus clairement qu'un try/except/pass :
from contextlib import suppress
with suppress(FileNotFoundError): os.remove("cache.tmp") # si le fichier n'existe pas, on ignoreÀ retenir
Section intitulée « À retenir »- Le code risqué va dans
try, la réaction dansexcept: le programme ne s'arrête plus brutalement. - Ciblez le type précis (
ValueError,KeyError) plutôt qu'unexceptgénérique qui masque tout. elses'exécute si aucune erreur,finallydans tous les cas (nettoyage, libération de ressources).raiselève une erreur ;raise ... fromconserve la cause d'origine pour le débogage.- Une exception personnalisée (classe héritant de
Exception) clarifie les erreurs métier. - Les assertions servent au développement, pas à la gestion d'erreurs en production (désactivables avec
-O). - En production, journalisez avec
logging.exception()au lieu deprint(), et n'avalez jamais une erreur en silence. - Depuis Python 3.11,
add_note()enrichit une erreur de contexte etexcept*trie les groupes d'exceptions (utile en asyncio).
FAQ : les exceptions en Python
Section intitulée « FAQ : les exceptions en Python »try et la réaction dans except :try:
nombre = int(input("Un nombre : "))
resultat = 10 / nombre
except ZeroDivisionError:
print("Division par zéro impossible.")
except ValueError:
print("Ce n'est pas un nombre valide.")
Le programme n'est plus interrompu : il exécute le except correspondant puis continue. Ciblez toujours un type précis (ValueError, KeyError) plutôt qu'un except générique, pour ne pas masquer d'autres erreurs.raise lève explicitement une exception :def verifier_age(age):
if age < 0:
raise ValueError("L'âge ne peut pas être négatif.")
L'appelant la capture avec try/except. Pour conserver l'erreur d'origine en la relançant sous une autre forme, utilisez raise ... from :try:
open(chemin)
except FileNotFoundError as e:
raise RuntimeError("Config illisible") from e
Le chaînage garde la trace de la cause (__cause__), précieux pour déboguer.try :try:
fichier = open("donnees.txt")
except FileNotFoundError:
print("Fichier absent") # une exception a eu lieu
else:
print("Ouverture réussie") # AUCUNE exception
finally:
print("Nettoyage") # TOUJOURS, erreur ou non
except: une exception a été levée.else: letrys'est déroulé sans erreur.finally: s'exécute dans tous les cas, idéal pour libérer des ressources.
as, puis exploitez la variable :try:
int("abc")
except ValueError as e:
print(f"Erreur : {e}") # Erreur : invalid literal for int()...
str(e) ou une f-string donnent le message. Dans un vrai programme, préférez logging.exception(), qui journalise le message et la pile d'appels complète :import logging
try:
10 / 0
except ZeroDivisionError:
logging.exception("Échec du calcul")
Exception :class TemperatureError(Exception):
"""Levée quand la température est invalide."""
def verifier(temp):
if temp < -273.15:
raise TemperatureError("Sous le zéro absolu.")
try:
verifier(-300)
except TemperatureError as e:
print(f"Erreur : {e}")
Une exception métier rend le code plus clair et permet à l'appelant de traiter spécifiquement vos cas. Nommez-la avec le suffixe Error par convention.# À ÉVITER : masque toutes les erreurs, même imprévues
try:
traiter()
except Exception:
pass
Ce pass silencieux laisse le programme continuer dans un état incohérent et rend le débogage très difficile. Il peut même cacher un bug de sécurité.Capturez le type précis attendu. Si vous devez attraper large (dans une boucle de service, par exemple), journalisez au minimum :except Exception:
logging.exception("Erreur inattendue")
raise
Prochaines étapes
Section intitulée « Prochaines étapes »La gestion des erreurs se combine avec presque tout : lecture de fichiers, appels d'API et journalisation propre en font un réflexe quotidien.