Exceptions#
courant
try / except / else / finally#
try:
resultat = 10 / diviseur
except ZeroDivisionError:
print("division par zéro")
except (TypeError, ValueError) as e:
print(f"erreur : {e}")
else:
print("aucune exception levée") # exécuté seulement si pas d'exception
finally:
print("toujours exécuté") # nettoyage (fermeture ressource, etc.)
Hiérarchie des exceptions courantes#
BaseException
├── KeyboardInterrupt (Ctrl+C — ne jamais l'attraper par accident)
├── SystemExit (sys.exit())
└── Exception (toutes les erreurs « normales »)
├── ArithmeticError
│ ├── ZeroDivisionError
│ └── OverflowError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── OSError
│ ├── FileNotFoundError
│ ├── PermissionError
│ └── IsADirectoryError
├── ValueError (bon type, valeur inexploitable : int("abc"))
├── TypeError (mauvais type : "a" + 1)
├── AttributeError
├── NameError
└── StopIteration
Attraper une exception attrape aussi toutes ses sous-classes : except LookupError: intercepte IndexError et KeyError, except OSError: intercepte FileNotFoundError.
Exception.__mro__ # remonter la chaîne d'héritage d'une exception
issubclass(KeyError, LookupError) # True
Piège
Ne jamais utiliser except: nu (attrape aussi KeyboardInterrupt, SystemExit). Toujours préciser le type, ou au minimum except Exception:.
Lever une exception#
def diviser(a, b):
if b == 0:
raise ZeroDivisionError("b ne peut pas être nul")
return a / b
raise ValueError("message") from erreur_originale # chaînage explicite
Exceptions personnalisées#
class SoldeInsuffisantError(Exception):
"""Levée quand un retrait dépasse le solde disponible."""
def retirer(compte, montant):
if montant > compte.solde:
raise SoldeInsuffisantError(f"solde {compte.solde} < {montant}")
assert#
Désactivé si Python est lancé avec -O : ne pas l'utiliser pour valider des entrées utilisateur, seulement pour du débogage / des invariants internes.