Skip to content

Exceptions en Python

Les erreurs sont inévitables en programmation. Python offre des mécanismes robustes pour gérer les erreurs d’exécution (runtime errors) avec élégance, évitant ainsi que votre programme ne plante de manière inattendue.

  • Gestion des exceptions (Exception Handling) : Utilisation des blocs try, except, else et finally pour gérer les erreurs au fur et à mesure qu’elles se produisent.
  • Assertions : Utilisation de l’instruction assert pour des auto-vérifications internes pendant le développement et le débogage.

Une exception (exception) est un événement qui se produit pendant l’exécution d’un programme et qui perturbe le déroulement normal des instructions. Lorsque Python rencontre une condition d’erreur (comme une division par zéro ou une tentative d’accès à un fichier inexistant), il crée un objet exception.

Si cet objet exception n’est pas ‘capturé’ et géré par votre code, le programme se termine et affiche une traceback (un message d’erreur indiquant où l’erreur s’est produite).

Python dispose de nombreuses exceptions intégrées pour les erreurs courantes. Les comprendre aide à écrire des blocs except spécifiques. Voici quelques exemples :

Nom de l’exceptionDescription
ExceptionLa classe de base pour la plupart des exceptions intégrées qui ne provoquent pas l’arrêt du système.
AttributeErrorLevée lorsqu’une référence ou une affectation d’attribut échoue (par exemple, mylist.methode_non_existante()).
ImportErrorLevée lorsqu’une instruction import ne parvient pas à trouver le module ou un nom dans le module.
ModuleNotFoundErrorSous-classe d’ImportError, levée lorsqu’un module ne peut pas être localisé.
IndexErrorLevée lorsqu’un indice de séquence (index) est hors de portée (par exemple, mylist[10] alors que la liste n’a que 3 éléments).
KeyErrorLevée lorsqu’une clé de dictionnaire n’est pas trouvée (par exemple, mydict['cle_non_existante']).
NameErrorLevée lorsqu’un nom local ou global (variable, fonction) n’est pas trouvé.
TypeErrorLevée lorsqu’une opération ou une fonction est appliquée à un objet de type inapproprié (par exemple, '2' + 2).
ValueErrorLevée lorsqu’une opération ou une fonction reçoit un argument du bon type mais d’une valeur inappropriée (par exemple, int('abc')).
ZeroDivisionErrorLevée lorsque le deuxième argument d’une opération de division ou de modulo est zéro.
FileNotFoundErrorSous-classe d’OSError, levée lors d’une tentative d’ouverture d’un fichier qui n’existe pas.
OSErrorClasse de base pour les erreurs liées au système d’exploitation (comme fichier non trouvé, permission refusée).
KeyboardInterruptLevée lorsque l’utilisateur appuie sur la touche d’interruption (généralement Ctrl+C).

Pour gérer les erreurs potentielles, vous placez le code susceptible de lever une exception à l’intérieur d’un bloc try. Vous le faites ensuite suivre d’un ou plusieurs blocs except pour spécifier comment gérer les différents types d’exceptions.

try:
# Code susceptible de lever une exception
# ... opérations ...
result = 10 / risky_variable
file = open("maybe_missing.txt", "r")
# ... plus d'opérations ...
except ZeroDivisionError:
# Gérer spécifiquement la division par zéro
print("Erreur : Impossible de diviser par zéro !")
result = 0
except FileNotFoundError:
# Gérer spécifiquement le fichier manquant
print("Erreur : Fichier non trouvé !")
# Peut-être fournir une valeur par défaut ou quitter
except Exception as e:
# Capturer toute autre exception qui hérite d'Exception
print(f"Une erreur inattendue s'est produite : {e}")
# Journaliser l'erreur, fournir un message générique, etc.
# Le code ici continue l'exécution si une exception a été capturée

Points clés :

  • Python essaie d’exécuter le code dans le bloc try.
  • Si une exception se produit, Python arrête l’exécution du bloc try et recherche un bloc except correspondant.
  • Il vérifie les blocs except dans l’ordre. Le premier dont le type correspond à l’exception levée (ou qui est une classe de base de celle-ci) est exécuté.
  • Si un bloc except correspondant est trouvé et exécuté, le programme continue après la structure try...except (sauf si le bloc except lui-même lève une exception ou quitte).
  • Si aucun bloc except correspondant n’est trouvé, l’exception se propage vers le haut de la pile d’appels (call stack). Si elle n’est pas capturée, le programme se termine.
  • Utiliser un type d’exception spécifique (ZeroDivisionError) est généralement préférable à un type général (Exception) car cela permet une gestion des erreurs adaptée et évite de capturer accidentellement des erreurs non liées.
  • La clause as e capture l’objet exception dans la variable e, permettant d’accéder aux détails de l’erreur.

Vous pouvez ajouter un bloc else facultatif après tous les blocs except. Le code du bloc else s’exécute uniquement si le bloc try se termine sans lever d’exceptions.

try:
# Opérations qui pourraient échouer
value = int(input("Enter a number: "))
result = 100 / value
except ValueError:
print("Invalid input. Please enter a number.")
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
# S'exécute uniquement si le bloc try réussit
print(f"Result is: {result}")
print("Operation successful.")
print("Program continues...")

Le bloc else est utile pour le code qui ne doit s’exécuter que lorsque le bloc try a réussi, le séparant du code potentiellement risqué.

Un bloc finally facultatif peut être ajouté. Le code du bloc finally s’exécute toujours, qu’une exception se soit produite dans le bloc try ou qu’elle ait été gérée par un bloc except. Il s’exécute même si le bloc try ou except utilise return, break ou continue.

file = None # Initialiser la variable de fichier en dehors du bloc try
try:
print("Attempting to open file...")
file = open("my_data.txt", "w")
file.write("Writing important data!")
# Simuler une erreur pouvant survenir après l'ouverture
# result = 10 / 0
except FileNotFoundError:
print("Error: Could not find the file path to write.")
except Exception as e:
print(f"An error occurred during writing: {e}")
finally:
# Ce bloc s'exécute toujours pour le nettoyage
print("Entering finally block...")
if file and not file.closed:
print("Fermeture du fichier.")
file.close()
else:
print("Le fichier n'a pas été ouvert ou est déjà fermé.")
print("Program finished.")

Le bloc finally est typiquement utilisé pour des actions de nettoyage, comme la fermeture de fichiers ou de connexions réseau, garantissant que les ressources sont correctement libérées.

Note : Un bloc try doit avoir au moins une clause except ou finally. Il peut avoir des clauses except, une clause finally, ou les deux. Si les deux sont présentes, la clause finally s’exécute en dernier.

Comme vu dans les exemples avec except Exception as e:, vous pouvez capturer l’objet exception lui-même. Cet objet contient souvent des informations utiles sur l’erreur.

try:
x = int('not_a_number')
except ValueError as ve:
print(f"ValueError capturée !")
print(f"Détails de l'erreur : {ve}") # Affiche le message d'erreur standard associé à l'exception
print(f"Arguments : {ve.args}") # Les exceptions stockent des arguments, incluant souvent le message

Ceci produit une sortie similaire à :

Caught a ValueError!
Error details: invalid literal for int() with base 10: 'not_a_number'
Arguments: ("invalid literal for int() with base 10: 'not_a_number'",)

Vous pouvez également lever délibérément des exceptions dans votre propre code à l’aide de l’instruction raise. Ceci est utile lorsque vous détectez une condition d’erreur qui empêche votre fonction ou méthode de remplir son contrat.

raise ExceptionType("Optional descriptive message about the error")
def check_positive(number):
if number <= 0:
# Lever une exception spécifique et appropriée
raise ValueError("Input must be a positive number.")
print(f"{number} is positive.")
try:
check_positive(10)
check_positive(-5)
except ValueError as e:
print(f"Error caught: {e}")

La meilleure pratique est de lever des exceptions intégrées spécifiques (comme ValueError, TypeError) lorsque cela est approprié, ou de créer des exceptions personnalisées pour les erreurs spécifiques à l’application.

Note : Lever des exceptions en utilisant des chaînes de caractères (raise "Niveau invalide !") est obsolète et ne doit pas être utilisé dans le Python moderne.

Pour les erreurs spécifiques à votre application ou bibliothèque, vous pouvez créer des classes d’exception personnalisées en héritant de Exception ou d’une classe d’exception intégrée plus spécifique.

# Définir une classe d'exception personnalisée
class InsufficientFundsError(Exception):
"""Exception personnalisée pour les cas où un compte a des fonds insuffisants."""
def __init__(self, balance, required):
self.balance = balance
self.required = required
message = f"Attempted to withdraw {required}, but only {balance} available."
super().__init__(message) # Appeler le constructeur de la classe de base
# Exemple d'utilisation
account_balance = 100
withdrawal_amount = 150
try:
if withdrawal_amount > account_balance:
raise InsufficientFundsError(account_balance, withdrawal_amount)
else:
print("Withdrawal successful.")
# account_balance -= withdrawal_amount
except InsufficientFundsError as e:
print(f"Transaction Failed: {e}")
# Accéder aux attributs personnalisés si nécessaire :
# print(f"Current Balance: {e.balance}, Amount Required: {e.required}")

Les exceptions personnalisées rendent votre gestion des erreurs plus spécifique et informative.

Une assertion (assertion) est une aide au débogage qui teste une condition. Si la condition est True, rien ne se passe. Si la condition est False, l’instruction assert lève une exception AssertionError avec un message facultatif.

Les assertions sont principalement utilisées pour vérifier les erreurs internes ou les violations d’hypothèses (erreurs de programmeur), et non pour les conditions d’erreur attendues (comme une entrée utilisateur invalide ou des fichiers manquants, qui devraient être gérées par try...except).

assert condition, "Optional message if condition is False"
def calculate_discount(price, discount_percentage):
# Vérification des préconditions : s'assurer que les entrées sont valides pour la logique
assert price >= 0, "Price cannot be negative"
assert 0 <= discount_percentage <= 100, "Discount must be between 0 and 100"
discount_amount = price * (discount_percentage / 100)
final_price = price - discount_amount
# Vérification post-condition : s'assurer que le résultat est logique
assert 0 <= final_price <= price, "Final price calculation seems wrong"
return final_price
# Appel valide
print(f"Discounted price: {calculate_discount(100, 10)}")
# Appel invalide - lèvera une AssertionError
try:
print(calculate_discount(100, 110))
except AssertionError as e:
print(f"Assertion Failed: {e}")

Note importante : Les assertions peuvent être désactivées globalement en Python en exécutant l’interpréteur avec l’option -O (optimiser) (python -O votre_script.py). Par conséquent, elles ne doivent pas être utilisées pour la logique de gestion des erreurs qui doit toujours être présente.