Gestion des exceptions en Python pour un code robuste

La gestion des exceptions en Python est essentielle pour créer des applications fiables. Ce mécanisme permet de gérer les erreurs de manière élégante, évitant les arrêts brusques du programme. Dans cet article, nous explorerons des techniques pour anticiper et résoudre les problèmes courants, renforçant ainsi la résilience du code.

Fondamentaux des exceptions

En programmation, une exception survient lorsqu'une opération échoue. Par exemple, diviser par zéro ou accéder à un index inexistant dans une liste déclenche une exception. Python offre une hiérarchie d'exceptions intégrées, telles que ValueError et IndexError, qui aident à identifier la nature du problème.

Il est important de distinguer les erreurs de syntaxe (SyntaxError) des exceptions (Exception). Les premières sont détectées à la compilation, tandis que les secondes se produisent à l'exécution. La classe de base Exception sert de parent pour la plupart des exceptions personnalisables.

Capture basique des exceptions

Le bloc try contient le code susceptible de générer une erreur. Si une exception se produit, l'exécution passe au bloc except correspondant. Voici un exemple illustrant la capture d'une division par zéro :


try:
    quotient = 8 / 0
except ZeroDivisionError:
    print("Division par zéro non autorisée.")

Pour gérer plusieurs types d'exceptions, utilisez des blocs except distincts :


try:
    nombre = int(input("Entrez un nombre : "))
except ValueError:
    print("Entrée invalide. Veuillez entrer un nombre entier.")
except Exception as erreur:
    print(f"Erreur inattendue : {erreur}")

Le bloc else s'exécute uniquement si aucune exception n'est levée, tandis que finally graantit l'exécution d'une nettoyage, même en cas d'erreur :


try:
    fichier = open("donnees.txt", "r")
    contenu = fichier.read()
except FileNotFoundError:
    print("Fichier introuvable.")
else:
    print("Lecture réussie.")
finally:
    fichier.close()
    print("Opération terminée.")

Exceptions personnalisées

Pour des besoins spécifiques, créez des exceptions héritant de Exception. Cela permet de fournir des messages d'erreur détaillés adaptés au contexte :


class ErreurSoldeInsuffisant(Exception):
    def __init__(self, article, solde, requis):
        self.article = article
        self.solde = solde
        self.requis = requis
        self.message = f"Solde de {solde} insuffisant pour acheter {article} (requis : {requis})."
        super().__init__(self.message)

def acheter_article(article, solde):
    prix = {"épée": 150, "bouclier": 80}
    cout = prix.get(article, 0)
    if solde < cout:
        raise ErreurSoldeInsuffisant(article, solde, cout)
    print(f"Achat réussi de {article}.")

try:
    acheter_article("épée", 100)
except ErreurSoldeInsuffisant as e:
    print(e)

Techniques avancées

Utilisez as pour accéder à l'instance d'exception et obtenir des détails supplémentaires :


try:
    resultat = 5 / 0
except ZeroDivisionError as exc:
    print(f"Type d'erreur : {type(exc).__name__}, Message : {exc}")

Les gestionnaires de contexte avec with automatisent la gestion des ressources. Ils garantissent le nettoyage même en cas d'exception :


class FichierGere:
    def __enter__(self):
        self.fichier = open("test.txt", "w")
        return self.fichier
    def __exit__(self, type_exc, val_exc, tb_exc):
        self.fichier.close()
        if type_exc:
            print(f"Exception attrapée : {val_exc}")

with FichierGere() as f:
    f.write("Données test.")
    # Une exception ici serait gérée proprement.

La chaîne d'exceptions via raise permet de relancer une exception après traitement, en préservant le contexte initial :


def traiter_donnees(valeur):
    try:
        resultat = 10 / int(valeur)
    except ValueError:
        print("Conversion échouée.")
        raise  # Relance l'exception originale.
    except ZeroDivisionError:
        print("Division par zéro détectée.")
        raise ValueError("Donnée invalide") from ZeroDivisionError

Bonnes pratiques

Préférez des captures précises d'exceptions à des clauses trop larges. Cela évite de masquer des erreurs imprévues :


try:
    operation_risquee()
except (KeyError, TypeError) as err:
    print(f"Erreur spécifique : {err}")
# Éviter "except:" sans spécification.

Intégrez la journalisation pour tracer les exceptions en production :


import logging
logging.basicConfig(level=logging.ERROR)

try:
    calcul = 1 / 0
except ZeroDivisionError:
    logging.error("Échec de division", exc_info=True)

Enfin, concevez des fonctions qui gèrent les exceptions de manière cohérente, en retournant des valeurs par défaut ou en levant des exceptions personnalisées pour maintenir la stabilité du système.

Étiquettes: Python gestion des exceptions try-except exceptions personnalisées contexte de gestion

Publié le 31 mai à 16h16