Aller au contenu

Maîtriser la gestion des exceptions en Python

Mise à jour :

logo python

La gestion des exceptions est un aspect essentiel en Python qui permet de gérer les erreurs de manière élégante et efficace. Comprendre comment fonctionnent les exceptions aide à écrire des programmes plus robustes et à offrir une meilleure expérience utilisateur.

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

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 :

# ZeroDivisionError
print(10 / 0) # Provoque une ZeroDivisionError
# TypeError
print('5' + 3) # Provoque une TypeError
# ValueError
int('abc') # Provoque une ValueError
# IndexError
liste = [1, 2, 3]
print(liste[5]) # Provoque une IndexError
# KeyError
dictionnaire = {'a': 1, 'b': 2}
print(dictionnaire['c']) # Provoque une KeyError

Comprendre ces exceptions permet de les anticiper et de les gérer correctement dans le code.

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 exception
except NomDeLException:
# Code à exécuter en cas d'exception

Exemple :

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

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 / num
except (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

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

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 / num
except 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

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.

#2 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

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 120
print(calculer_factorielle(-3)) # Provoque une AssertionError

Attention : 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

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

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érateur et dé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.”

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 except sans 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:
# Code susceptible de provoquer une exception
except Exception:
pass # Mauvaise pratique, toutes les exceptions sont masquées

Exemple de bonne pratique :

try:
# Code susceptible de provoquer une exception
except SpecificException as e:
# Traitement approprié de l'exception
print(f"Erreur : {e}")

Conclusion

La gestion des exceptions est essentielle pour écrire des programmes robustes en Python. En maîtrisant les techniques pour gérer, lever et créer des exceptions, il est possible d’améliorer significativement la fiabilité et la maintenabilité du code. Une gestion appropriée des erreurs permet non seulement d’éviter les plantages, mais aussi d’offrir une meilleure expérience utilisateur.