logo oujood
🔍

Les exceptions personnalisées en Python

Python permet de définir ses propres types d'erreurs en créant des classes qui héritent de Exception. Ce cours vous montre comment et pourquoi le faire.

OUJOOD.COM

Python propose une quarantaine d'exceptions intégrées : ValueError, TypeError, FileNotFoundError… Elles couvrent les erreurs génériques. Mais dès que votre programme a sa propre logique métier — un compte bancaire qui ne peut pas être négatif, un âge qui doit rester dans une plage raisonnable, un identifiant qui doit respecter un format précis — les exceptions intégrées ne suffisent plus. Elles ne disent pas ce qui s'est passé dans votre contexte.

Les exceptions personnalisées règlent ce problème. Ce sont des classes Python qui héritent de Exception. Elles permettent de nommer les erreurs propres à votre application, de leur attacher des informations utiles, et de les intercepter précisément sans attraper tout le reste.

Rappel : comment fonctionne une exception en Python

Avant de créer les vôtres, voici comment Python gère les erreurs. Quand une instruction échoue, Python lève une exception avec raise. Le bloc try/except permet de l'intercepter et de réagir proprement.

  📋 Copier le code

try:
    resultat = 10 / 0
except ZeroDivisionError:
    print("Division par zéro impossible.")
# Résultat : Division par zéro impossible.

# On peut aussi lever une exception manuellement
age = -5
if age < 0:
    raise ValueError("L'âge ne peut pas être négatif.")

Créer une exception personnalisée simple

Une exception personnalisée est une classe qui hérite de Exception. Dans sa forme la plus simple, elle n'a besoin d'aucun contenu — le nom de la classe suffit à identifier l'erreur.

  📋 Copier le code

# Définition d'une exception personnalisée
class AgeInvalideError(Exception):
    pass

# Utilisation
def verifier_age(age):
    if age < 0 or age > 150:
        raise AgeInvalideError(f"Âge invalide : {age}")
    return age

try:
    verifier_age(-3)
except AgeInvalideError as e:
    print("Erreur :", e)
# Résultat : Erreur : Âge invalide : -3

Le mot-clé pass indique que la classe ne définit rien de plus que ce qu'elle hérite. C'est suffisant dans beaucoup de cas : le nom de l'exception est déjà une information en soi.

Ajouter un message et des attributs personnalisés

Quand vous avez besoin de transporter plus d'informations avec l'erreur — le code d'erreur, la valeur fautive, un lien vers la documentation — définissez un constructeur __init__.

  📋 Copier le code

class SoldeInsuffisantError(Exception):
    def __init__(self, solde, montant):
        self.solde = solde
        self.montant = montant
        super().__init__(
            f"Solde insuffisant : vous avez {solde}€, "
            f"vous tentez de retirer {montant}€."
        )

def retirer(solde, montant):
    if montant > solde:
        raise SoldeInsuffisantError(solde, montant)
    return solde - montant

try:
    retirer(50, 120)
except SoldeInsuffisantError as e:
    print(e)
    print(f"Manque : {e.montant - e.solde}€")
# Résultat :
# Solde insuffisant : vous avez 50€, vous tentez de retirer 120€.
# Manque : 70€

L'appel à super().__init__(message) transmet le message d'erreur à la classe parente Exception, ce qui permet de l'afficher normalement avec print(e).

Organiser ses exceptions en hiérarchie

Quand un projet grossit, il est utile de regrouper les exceptions liées sous une exception parente commune. Cela permet d'attraper soit une erreur précise, soit toute la famille d'un coup.

  📋 Copier le code

# Exception parente pour toute l'application
class AppError(Exception):
    pass

# Exceptions spécifiques qui héritent d'AppError
class UtilisateurInexistantError(AppError):
    pass

class MotDePasseIncorrectError(AppError):
    pass

# On peut attraper une erreur précise...
try:
    raise MotDePasseIncorrectError("Mot de passe erroné.")
except MotDePasseIncorrectError as e:
    print("Authentification échouée :", e)

# ...ou toute la famille d'un coup
try:
    raise UtilisateurInexistantError("Utilisateur 'bob' introuvable.")
except AppError as e:
    print("Erreur applicative :", e)
# Résultat : Erreur applicative : Utilisateur 'bob' introuvable.

Lever une exception depuis une autre exception

Python permet d'enchaîner les exceptions avec raise ... from .... C'est utile quand vous interceptez une erreur bas niveau et que vous voulez la transformer en erreur métier, tout en conservant la trace de l'erreur originale.

  📋 Copier le code

class FichierConfigError(Exception):
    pass

def charger_config(chemin):
    try:
        with open(chemin) as f:
            return f.read()
    except FileNotFoundError as e:
        raise FichierConfigError(
            f"Impossible de charger la config : {chemin}"
        ) from e

try:
    charger_config("/chemin/inexistant/config.json")
except FichierConfigError as e:
    print(e)
# Résultat : Impossible de charger la config : /chemin/inexistant/config.json
# Python affiche aussi la cause originale (FileNotFoundError) dans le traceback

Bonnes pratiques

Quelques règles simples pour des exceptions propres et maintenables :

  • Nommez vos exceptions avec le suffixe Error — c'est la convention Python : ValidationError, ConnexionError, FormatError.
  • Héritez de Exception, pas de BaseExceptionBaseException est la racine de tout, y compris KeyboardInterrupt et SystemExit. L'attraper accidentellement peut bloquer l'arrêt du programme.
  • Créez une exception parente par module ou domaine — cela simplifie les except dans le code appelant.
  • Ne créez pas une exception pour chaque cas — si ValueError ou TypeError décrit déjà l'erreur clairement, utilisez-les.

  📋 Copier le code

# Structure recommandée pour un projet Python

class MonProjetError(Exception):
    """Exception de base pour MonProjet."""
    pass

class ValidationError(MonProjetError):
    """Données invalides."""
    pass

class RessourceIntrouvableError(MonProjetError):
    """Ressource demandée absente."""
    pass

# Dans le code métier
def trouver_produit(id_produit, catalogue):
    if id_produit not in catalogue:
        raise RessourceIntrouvableError(
            f"Produit '{id_produit}' absent du catalogue."
        )
    return catalogue[id_produit]

catalogue = {"P001": "Clavier", "P002": "Souris"}

try:
    produit = trouver_produit("P099", catalogue)
except RessourceIntrouvableError as e:
    print(e)
# Résultat : Produit 'P099' absent du catalogue.

Par carabde | Mis à jour le 21 avril 2026