Aller au contenu principal

Exceptions

Progression

#Exceptions

Les exceptions constituent le mécanisme privilégié en Python pour signaler proprement les situations anormales et garantir la libération correcte des ressources. Elles permettent de séparer la logique métier du traitement des erreurs, rendant le code plus lisible et plus robuste. Python offre une structure complète avec try/except/else/finally, des hiérarchies d'exceptions spécifiques, le chaînage d'exceptions, et des context managers pour automatiser le nettoyage des ressources.

Objectifs d'apprentissage

L'objectif est d'apprendre à utiliser correctement les blocs try/except/else/finally en fonction de l'intention (gestion d'erreur ou nettoyage de ressources). Vous apprendrez également à lever des exceptions précises et à les chaîner avec raise ... from e pour préserver le contexte d'erreur. Enfin, vous maîtriserez l'écriture de context managers avec l'instruction with pour garantir la libération automatique des ressources.

pythonpython
1try:2    1/03except ZeroDivisionError:4    print('division par zéro !')5finally:6    print('Toujours exécuté')
Spécificité d'abord

Interceptez des exceptions précises (ex: ValueError, KeyError) et laissez les autres remonter. Évitez except Exception sans re‑raise ciblé.

Lever des exceptions personnalisées permet de communiquer clairement les problèmes métier. Il est important de choisir un type d'exception approprié ou de créer ses propres classes d'exception pour distinguer les différentes catégories d'erreurs :

pythonpython
1def racine(n: float) -> float:2    if n < 0:3        raise ValueError('n doit être >= 0')4    return n ** 0.55 6print(racine(9))

Le chaînage d'exceptions avec raise ... from e permet de préserver la trace complète de l'erreur d'origine tout en enveloppant l'exception dans un contexte métier plus significatif. Cette pratique facilite grandement le diagnostic des problèmes en production :

pythonpython
1class PaymentError(Exception):2    pass3 4def pay():5    try:6        1/07    except ZeroDivisionError as e:8        raise PaymentError('échec paiement') from e9 10try:11    pay()12except PaymentError as e:13    print('Business error:', e.__cause__.__class__.__name__)

#Playground

Chargement de l’éditeur...

#Exercices

Ces exercices vous permettront de pratiquer la gestion robuste des exceptions :

Commencez par écrire un parseur robuste capable de lire des lignes au format a;b;c, ignorant intelligemment les lignes invalides tout en produisant un rapport d'erreurs détaillé pour faciliter le débogage. Ensuite, implémentez un context manager timer() qui mesure automatiquement la durée d'exécution d'un bloc de code lorsqu'il est utilisé avec l'instruction with, démontrant ainsi la puissance de cette abstraction pour la gestion des ressources.

#Pièges fréquents

Plusieurs pièges classiques guettent les développeurs lors de la gestion des exceptions. Utiliser except Exception: de manière trop large peut masquer des bugs réels en interceptant des erreurs inattendues. Il est crucial de ne pas oublier le bloc finally (ou mieux, un context manager) pour garantir la libération des fichiers, verrous ou sockets, même en cas d'erreur. Enfin, utiliser return dans un bloc finally supprime l'exception en cours de propagation, ce qui peut masquer des problèmes graves.

#Danger: return dans finally

pythonpython
1def f():2    try:3        1/04    finally:5        return 'masque l\'exception'  # à éviter6 7print(f())  # L'exception ZeroDivisionError est perdue !

Règle simple : ne jamais retourner une valeur depuis finally. Effectuez les nettoyages nécessaires, puis laissez l'exception remonter naturellement vers l'appelant.

#Relancer proprement

Lorsque vous interceptez une exception pour ajouter du contexte, utilisez le chaînage avec from pour préserver la trace complète de l'erreur d'origine :

pythonpython
1try:2    ouvrir_fichier()3except OSError as e:4    # Ajouter du contexte sans perdre la trace d'origine5    raise RuntimeError('échec ouverture fichier config') from e

#Nettoyages et context managers

Les context managers, utilisés avec l'instruction with, garantissent automatiquement la fermeture ou la libération des ressources, même en cas d'exception. Cette approche est plus sûre et plus lisible que la gestion manuelle dans des blocs finally :

pythonpython
1from contextlib import contextmanager2 3@contextmanager4def timer():5    import time6    t0=time.perf_counter();7    try:8        yield9    finally:10        print(f"{(time.perf_counter()-t0)*1e3:.1f} ms")11 12with timer():13    sum(range(10_000))

#Quiz éclair

Lequel garantit l'exécution même en cas d'exception ?
Lequel garantit l'exécution même en cas d'exception ?