La gestion des exceptions en Python

25 Apr 2019 25 Apr 2019 12866 vues ESSADDOUKI Mostafa 9 min de lecture

Gestion des exceptions

Comme d'autres langages, Python fournit un mécanisme de gestion d'exceptions via les blocs try / except. Une exception est un événement qui survient pendant l'exécution et interrompt le flux normal du programme.

Définition — Exception Une exception est une erreur détectée à l'exécution (contrairement aux erreurs de syntaxe détectées à l'analyse). Lorsqu'une exception n'est pas gérée, Python l'affiche sous forme de traceback et interrompt le programme.
Type d'erreurDescriptionDétectée quand ?Exemple
Erreur de syntaxeL'analyseur Python ne comprend pas la ligneAvant l'exécutionif x = 5:
ExceptionErreur survenant pendant l'exécutionÀ l'exécution1 / 0

Exceptions standard les plus fréquentes

ExceptionCauseExemple déclencheur
ZeroDivisionErrorDivision par zéro5 // 0
TypeErrorType d'argument incorrect"2" + 2
ValueErrorValeur incorrecte pour le typeint("abc")
IndexErrorIndice hors limites[1,2][5]
KeyErrorClé absente dans un dictionnaired["x"] si "x" absent
AttributeErrorAttribut ou méthode inexistantobj.inconnu
ImportErrorModule introuvableimport inexistant
FileNotFoundErrorFichier introuvableopen("x.txt")
RecursionErrorProfondeur de récursion dépasséeRécursion infinie
MemoryErrorMémoire insuffisanteAllocation trop grande

Bloc try / except


Syntaxe complète Python
try:
    # code susceptible de lever une exception
except TypeException1:
    # gestion de TypeException1
except (TypeException2, TypeException3):
    # gestion de plusieurs exceptions à la fois
except Exception as e:
    # gestion générique — e contient le message d'erreur
else:
    # exécuté UNIQUEMENT si aucune exception n'a été levée
finally:
    # exécuté TOUJOURS, exception ou non
Fonctionnement du bloc try
  1. Le bloc try est exécuté en premier.
  2. Si aucune exception ne survient → le bloc except est ignoré.
  3. Si une exception survient → le reste du bloc try est ignoré, Python cherche le except correspondant.
  4. Si aucun except ne correspond → l'exception est propagée aux blocs try extérieurs.
  5. Si elle n'est jamais gérée → le programme s'arrête avec un traceback.

Exemple — Division avec gestion d'exception

def division(a, b):
    try:
        resultat = a // b
        print(f"{a} // {b} = {resultat}")
    except ZeroDivisionError:
        print("Erreur : division par zéro !")

print("Première instruction")
division(4, 2)

print("Deuxième instruction")
division(4, 0)
Sortie
Première instruction
4 // 2 = 2
Deuxième instruction
Erreur : division par zéro !

Exemple — Plusieurs clauses except

Une instruction try peut avoir plusieurs clauses except pour gérer différentes exceptions. Au plus un gestionnaire sera exécuté par exception.

def convertir(valeur):
    try:
        nombre = int(valeur)
        resultat = 100 // nombre
        print(f"100 // {nombre} = {resultat}")
    except ZeroDivisionError:
        print("Erreur : division par zéro !")
    except ValueError:
        print(f"Erreur : '{valeur}' n'est pas un entier valide.")
    except TypeError:
        print("Erreur : type incorrect.")

convertir("5")     # OK
convertir("0")     # ZeroDivisionError
convertir("abc")   # ValueError
convertir(None)    # TypeError
Sortie
100 // 5 = 20
Erreur : division par zéro !
Erreur : 'abc' n'est pas un entier valide.
Erreur : type incorrect.

Clauses else et finally

Clause else

Le bloc else est exécuté uniquement si aucune exception n'a été levée dans le bloc try. Il permet de séparer le code "normal" du code de gestion d'erreur.

Exemple — try / except / else

def division(a, b):
    try:
        resultat = a // b
    except ZeroDivisionError:
        print("Erreur : division par zéro !")
    else:
        print(f"Résultat : {resultat}")   # seulement si pas d'exception

division(10, 2)   # Résultat : 5
division(10, 0)   # Erreur : division par zéro !
Sortie
Résultat : 5
Erreur : division par zéro !

Clause finally

Le bloc finally est toujours exécuté, qu'une exception ait été levée ou non. Il est typiquement utilisé pour libérer des ressources (fermer un fichier, une connexion, etc.).

Exemple — try / except / finally

def lire_fichier(chemin):
    fichier = None
    try:
        fichier = open(chemin, 'r')
        contenu = fichier.read()
        print(contenu)
    except FileNotFoundError:
        print(f"Fichier '{chemin}' introuvable.")
    finally:
        if fichier:
            fichier.close()
            print("Fichier fermé.")   # exécuté dans tous les cas

lire_fichier("data.txt")
Astuce — Préférer with Pour la gestion de ressources (fichiers, connexions), le gestionnaire de contexte with est plus élégant que finally: il garantit la fermeture automatique même en cas d'exception.
with open("data.txt", "r") as f:
    contenu = f.read()   # f.close() est appelé automatiquement
ClauseExécutée quand ?Usage typique
tryToujours en premierCode susceptible de lever une exception
exceptSi une exception correspondGestion de l'erreur
elseSi aucune exceptionCode "succès" isolé du try
finallyToujoursLibération de ressources

Lever une exception — raise

L'instruction raise permet de forcer manuellement une exception. Elle est utile pour valider des données ou signaler explicitement une condition d'erreur.


Syntaxe Python
raise TypeException("message d'erreur")   # lever avec message
raise                                      # re-lever l'exception courante

Exemple — Lever et capturer une exception

def division(a, b):
    try:
        if b == 0:
            raise ZeroDivisionError("Division par zéro interdite !")
        return a // b
    except ZeroDivisionError as e:
        print(f"Erreur interceptée : {e}")
        raise   # re-propage l'exception vers l'appelant

try:
    division(4, 0)
except ZeroDivisionError:
    print("Erreur confirmée dans le bloc appelant.")
Sortie
Erreur interceptée : Division par zéro interdite !
Erreur confirmée dans le bloc appelant.

Exemple — Validation de données avec raise

def set_age(age):
    if not isinstance(age, int):
        raise TypeError(f"L'âge doit être un entier, reçu : {type(age).__name__}")
    if age < 0 or age > 150:
        raise ValueError(f"Âge invalide : {age}. Attendu entre 0 et 150.")
    print(f"Âge défini : {age}")

set_age(25)       # OK
set_age(-5)       # ValueError
set_age("vingt")  # TypeError
Sortie
Âge défini : 25
ValueError: Âge invalide : -5. Attendu entre 0 et 150.
TypeError: L'âge doit être un entier, reçu : str

Intercepter toutes les exceptions

On peut utiliser la classe de base Exception pour capturer n'importe quelle exception et accéder à son message via as e.


Syntaxe Python
try:
    # code
except Exception as e:
    print(type(e).__name__, ":", e)   # nom + message de l'erreur

Exemple

def tester(valeur):
    try:
        resultat = 100 // int(valeur)
        print(f"Résultat : {resultat}")
    except Exception as e:
        print(f"[{type(e).__name__}] {e}")

tester("5")    # OK
tester("0")    # ZeroDivisionError
tester("abc")  # ValueError
Sortie
Résultat : 20
[ZeroDivisionError] integer division or modulo by zero
[ValueError] invalid literal for int() with base 10: 'abc'
Attention — Éviter le except trop large Capturer toutes les exceptions avec except Exception (ou pire, un except nu) peut masquer des erreurs de programmation. Préférez toujours cibler des exceptions spécifiques et réserver except Exception pour la journalisation ou les interfaces utilisateur.

Classes d'exceptions de base

Python organise ses exceptions dans une hiérarchie de classes. Connaître cette hiérarchie permet de choisir le bon niveau de capture.

Classe de baseSous-exceptionsDescription
ArithmeticErrorZeroDivisionError, OverflowError, FloatingPointErrorErreurs arithmétiques
LookupErrorIndexError, KeyErrorClé ou indice invalide
OSErrorFileNotFoundError, PermissionError, IOErrorErreurs liées au système d'exploitation
ValueErrorUnicodeErrorValeur incorrecte pour le type attendu
RuntimeErrorRecursionError, NotImplementedErrorErreurs générales d'exécution

ArithmeticError

Classe de base pour les erreurs arithmétiques : OverflowError, ZeroDivisionError, FloatingPointError.

def division(a, b):
    try:
        return a // b
    except ArithmeticError as e:
        print(f"[ArithmeticError] {e}")

division(10, 0)   # capturé par ArithmeticError
Sortie
[ArithmeticError] integer division or modulo by zero

LookupError

Classe de base pour IndexError et KeyError.

T = [1, 2, 3]
d = {"a": 1}

try:
    print(T[10])
except LookupError as e:
    print(f"[LookupError] {type(e).__name__} : {e}")

try:
    print(d["z"])
except LookupError as e:
    print(f"[LookupError] {type(e).__name__} : {e}")
Sortie
[LookupError] IndexError : list index out of range
[LookupError] KeyError : 'z'

AssertionError

Levée quand une instruction assert échoue. Utile pour les tests et la validation d'invariants.

def diviser_positif(a, b):
    assert b > 0, f"b doit être positif, reçu : {b}"
    return a / b

try:
    diviser_positif(10, -3)
except AssertionError as e:
    print(f"[AssertionError] {e}")
Sortie
[AssertionError] b doit être positif, reçu : -3

RecursionError

Levée quand la profondeur de récursion maximale est dépassée (par défaut 1000 en Python).

def infini(n):
    return infini(n - 1)   # pas de condition d'arrêt

try:
    infini(10)
except RecursionError as e:
    print(f"[RecursionError] {e}")
Sortie
[RecursionError] maximum recursion depth exceeded

Créer ses propres exceptions

On peut définir des exceptions personnalisées en héritant de la classe Exception. C'est une bonne pratique pour les bibliothèques et les applications complexes.

Exemple — Exception métier personnalisée

class NoteInvalideError(Exception):
    """Exception levée quand une note est hors de [0, 20]."""
    def __init__(self, note):
        self.note = note
        super().__init__(f"Note invalide : {note}. Doit être entre 0 et 20.")

def valider_note(note):
    if not (0 <= note <= 20):
        raise NoteInvalideError(note)
    print(f"Note valide : {note}")

try:
    valider_note(15)
    valider_note(25)
except NoteInvalideError as e:
    print(f"[Erreur] {e}")
Sortie
Note valide : 15
[Erreur] Note invalide : 25. Doit être entre 0 et 20.

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.