En Python, une exception représente une situation anormale qui interrompt le déroulement normal de l'exécution d'un programme. Contrairement aux erreurs de syntaxe (détectées lors de la lecture du code), les exceptions surviennent au moment de l'exécution. Elles permettent non seulement d'identifier les problèmes potentiels (division par zéro, accès à une clef inexistante dans un dictionnaire, etc.) mais surtout de les gérer de manière à éviter un arrêt brutal du programme.
La gestion des exceptions repose sur plusieurs constructions clés :
try: on place le code susceptible de provoquer une erreur à l'intérieur de ce bloc.except: permet de capturer et de traiter spécifiquement un ou plusieurs types d'exceptions.else: ce bloc optionnel s'exécute uniquement si aucun problème (aucune exception) n'est survenu dans le bloc try.finally: ce bloc optionnel s'exécute dans tous les cas, qu'une exception soit levée ou non, afin de nettoyer ou libérer des ressources.try/exceptLa forme la plus simple consiste à entourer le code potentiellement problématique avec un bloc try, suivi d'un ou plusieurs blocs except pour gérer les erreurs:
try:
# Code susceptible de générer une exception
nombre = int(input("Entrez un nombre : "))
resultat = 10 / nombre
print("Résultat :", resultat)
except ZeroDivisionError:
print("Erreur : division par zéro.")
except ValueError:
print("Erreur : vous devez saisir un nombre valide.")
Dans cet exemple, si l'utilisateur saisit une valeur non numérique, la conversion en entier lèvera une exception ValueError et le bloc correspondant s'exécutera. De même, si l'utilisateur saisit zéro, la division par zéro lèvera un ZeroDivisionError.
Remarque: il est important d'être toujours le plus spécifique possible en ce qui concerne le type d'exception.
Si le même traitement convient pour plusieurs types d'exceptions, on peut les regrouper sous la forme d'un tuple :
try:
resultat = 10 / int(input("Entrez un nombre : "))
print("Résultat :", resultat)
except (ZeroDivisionError, ValueError) as e:
print("Erreur :", e)
Ici, peu importe si c'est une division par zéro ou une mauvaise conversion, l'exception sera capturée et affichée.
elseLe bloc else s'exécute uniquement si aucune exception n'est levée dans le bloc try. Son utilité est de séparer clairement le code qui risque de poser problème de celui qui correspond au cas de succès. Cela permet d'éviter qu'un code « normal » susceptible de générer une erreur par inadvertance ne soit inclus dans le bloc try.
Exemple:
try:
nombre = int(input("Entrez un nombre : "))
except ValueError:
print("Erreur : veuillez saisir un nombre valide.")
else:
# Ce bloc ne s'exécute que si tout s'est bien passé dans le try
print(f"Vous avez saisi le nombre {nombre}.")
Ici, si l'utilisateur saisit bien une valeur numérique, c'est uniquement dans le bloc else que vous poursuivez le traitement. Cela permet de garder le code du traitement normal séparé de celui du traitement d'erreur.
finallyLe bloc finally est exécuté quand l'exception survienne ou non. Il est souvent utilisé pour libérer des ressources (fermer un fichier, libérer une connexion réseau, etc.) ou effectuer des actions de nettoyage indispensables, quelle que soit la réussite ou l'échec du bloc try.
Exemple:
try:
fichier = open("donnees.txt", "r")
contenu = fichier.read()
print("Contenu :", contenu)
except FileNotFoundError:
print("Erreur : fichier non trouvé.")
finally:
# Ce bloc sera exécuté quoiqu'il arrive : fermeture du fichier
try:
fichier.close()
except NameError:
# Si fichier n'a pas été ouvert
pass
print("Fermeture de la ressource terminée.")
Dans ce cas, même si le fichier n'existe pas et l'exception FileNotFoundError est levée, le bloc finally s'assure que toute opération (ici la tentative de fermeture) s'exécute, garantissant ainsi un nettoyage correct.
Source: note.nkmk.me - python-try-except-else-finally
Limitez le bloc try au strict minimum: ne placez dans le bloc try que les lignes susceptibles de lever l'exception que vous souhaitez gérer.
Utilisez else pour le code qui doit s'exécuter uniquement en cas de succès complet du bloc try.
Évitez l'usage de blocs except sans spécifier l'exception (ce qui attrape toutes les erreurs) afin de ne pas masquer des bogues inattendus.
# Mauvaise pratique
try:
# Code
except:
pass # masque toutes les erreurs
Si vous souhaitez récupérer toutes les erreurs "non fatales", utilisez plutôt :
except Exception as e:
print("Erreur :", e)
finally pour garantir le nettoyageToujours fermer les fichiers, libérer des connexions ou d'autres ressources dans un bloc finally, afin que ces actions soient effectuées même en cas d'exception.
Vous pouvez créer vos propres classes d'exception pour des cas spécifiques à votre application. Cela améliore la lisibilité et la gestion fine des erreurs.
class MonErreur(Exception):
pass
def valider_age(age):
if age < 0:
raise MonErreur("L'âge ne peut pas être négatif.")
else:
return True
Dans un code collaboratif, ou lorsque vous devez déboguer plus tard, commentez pourquoi un certain type d'exception est capturé et pourquoi telle ou telle branche est choisie.
Bien que le paradigme EAFP (« Easier to ask for forgiveness than permission ») est courant en Python et autorise l'utilisation d'exceptions pour contrôler le flux (par exemple, accéder directement à une clef d'un dictionnaire et gérer l'exception KeyError plutôt que de tester son existence), évitez de vous reposer trop lourdement sur cette technique dans des situations où une vérification préalable (LBYL - « Look Before You Leap ») rendrait le code plus clair.
# Exemple EAFP :
try:
valeur = mon_dict['clé']
except KeyError:
valeur = valeur_par_defaut
# Alternative LBYL :
valeur = mon_dict.get('clé', valeur_par_defaut)
Utilisez des tests unitaires pour vous assurer que votre code se comporte comme attendu face aux exceptions. Cela vous évite des surprises en production.
Voici un exemple qui intègre l'utilisation de try, except, else et finally :
class DivisionError(Exception):
"""Exception personnalisée pour la division."""
pass
def diviser(a, b):
try:
# Bloc à risque : la division et la conversion
a = float(a)
b = float(b)
resultat = a / b
except ZeroDivisionError as e:
print("Erreur : division par zéro.")
# On peut lever ici une exception personnalisée si besoin :
raise DivisionError("La division par zéro n'est pas permise.")
except ValueError:
print("Erreur : les valeurs doivent être numériques.")
raise DivisionError("Conversion impossible en float.")
else:
# Ce bloc s'exécute si aucune exception n'est survenue
print(f"Le résultat de la division est : {resultat}")
return resultat
finally:
# Ce bloc s'exécute toujours, pour assurer le nettoyage ou un message de fin
print("Fin de l'opération de division.")
# Exemples d'utilisation :
print("Exemple 1:")
try:
diviser(10, 2)
except DivisionError as de:
print(de)
print("\nExemple 2:")
try:
diviser(10, 0)
except DivisionError as de:
print(de)
print("\nExemple 3:")
try:
diviser("dix", 2)
except DivisionError as de:
print(de)
Ce que produit cet exemple :
try réussit, le bloc else affiche le résultat, puis le bloc finally s'exécute.ZeroDivisionError capturé, le bloc except affiche le message et relance (optionnellement) une exception personnalisée, puis le finally est exécuté.ValueError, le bloc except réagit, et encore une fois le finally est exécuté.La gestion des erreurs en Python grâce aux blocs try, except, else et finally est une technique essentielle pour rendre vos programmes robustes et maintenables. Utiliser intelligemment ces blocs permet de :
else).finally).