Découvrez les exceptions
Quiconque écrit dans n’importe quel langage de programmation depuis un certain temps a déjà rencontré une exception. Une exception est un message du programme qui signale que quelque chose s’est mal passé.
Les exceptions peuvent concerner toutes sortes de choses – vous avez peut-être essayé de diviser par zéro, ou fait une faute de frappe dans un nom de variable et essayé d’accéder à quelque chose qui n’existe pas, ou passé une chaîne à une fonction qui attendait un nombre.
Les exceptions sont déclenchées – ou levées, ou lancées – par un programme. Les exceptions que vous avez pu voir précédemment – comme NameError
, ZeroDivisionError
, ou IndexError
– sont toutes des exceptions intégrées qui sont lancées par les éléments internes de Python lui-même.
Nous pouvons également lancer des exceptions personnalisées dans nos propres programmes. Pour ce faire, on utilise le mot-cléraise
.
def get_half_even_number(number):
if number % 2 == 0:
return number / 2
else:
message = (
f"This Function only supports halving even numbers. Received: {number}"
)
raise Exception(message)
Dans cet exemple, nous déclenchons une exception avec un message – une chaîne qui s’affiche pour l’utilisateur quand l’exception se produit. Dans ce cas, l’exception donne des informations sur l’erreur en elle-même.
Toutes les exceptions Python sont des objets, ce qui mérite d’être souligné.Exception
est la classe de base des exceptions Python, dont tout hérite. Cela signifie que nous pouvons créer nos propres exceptions – ce que nous couvrirons après une note rapide sur la…
Gestion des exceptions
Lorsqu’une exception est déclenchée dans notre code – et qu’elle n’est pas gérée – habituellement, notre programme s’arrête.
Nous pouvons gérer une exception en utilisant untry-except
(souvent appelé une instruction try-catch dans d’autres langages).
def increase_percent(initial_value, after_value):
try:
return (after_value / initial_value) * 100
except ZeroDivisionError:
return 0
except Exception as error:
print("Uh oh, unexpected error occurred!")
raise error
Ici, nous avons une fonction,increase_percent
, qui calcule l’augmentation (ou la réduction !) du pourcentage à partir d’une valeur initiale. Notre calcul se trouve dans un bloc try
, ce qui signifie que si une exception se produit, elle sera gérée par nos blocs « except », au lieu de se contenter de planter.
Prenons un exemple. Le problème le plus évident qui pourrait survenir ici serait une erreur de division par zéro. Dans cette fonction, si une ZeroDivisionError
est lancée, nous l’attrapons et retournons 0 à la place, grâce au premier bloc d’exception. L’exception est officiellement gérée et le programme ne plante pas !
D’autre part, si l’exception qui se produit n’est pas une ZeroDivisionError
, nous la déclenchons simplement à nouveau. Vous voyez le as error
dans le deuxième bloc except ? Il assigne l’objet exception à la variable error, que nous pouvons alors redéclencher – soit pour qu’elle soit gérée par un autre bloc try dans lequel cette fonction a été appelée, soit simplement pour mettre fin au programme.
Lorsqu’une exception est levée, elle se propage dans le programme. Python ne fait plus les choses dans l’ordre, mais lance plutôt continuellement l’exception en remontant la pile – c’est-à-dire la pile de fonctions/méthodes qui ont appelé d’autres fonctions/méthodes. L’exception va continuer à remonter jusqu’à ce qu’elle soit gérée ou qu’elle n’ait plus nulle part où aller. Dans ce dernier cas, le programme plante.
Écrivez des exceptions personnalisées
Dans un programme complexe, de nombreuses choses peuvent mal se passer. Pour gérer des problèmes uniques, Python nous permet de définir nos propres exceptions personnalisées. Si nous gardons en tête que les exceptions sont des objets, nous pouvons définir nos propres classes d’Exceptions, exactement comme nous le ferions pour tout autre objet.
class InvalidAddressException(Exception):
"""Gère les exceptions liées aux mauvaises adresses."""
pass
class OwlContactSystem(ContactSystem):
def __init__(self, address):
if (not validate_address(address)):
raise InvalidAddressException(f"Adresse invalide: {address}"
self.address = address
Ici, nous utilisons notre OwlContactSystem
de tout à l’heure. Dans notre constructeur, nous appelons la fonction validate_address
avec la chaîne d’adresse e-mail qui est donnée. Si la validation de l’adresse e-mail échoue, nous déclenchons une InvalidAddressException
– qui aura probablement besoin d’être gérée par quiconque appelle ce constructeur.
Mais InvalidAddressException
ne fait rien, alors pourquoi l’utiliser ?
De nombreuses exceptions personnalisées ressemblent à cela. Bien qu’il soit possible de surcharger des méthodes dans une exception personnalisée, le simple fait de disposer d’un type séparé nous est en réalité très utile.
Nous pouvons aussi mettre un message par défaut, ce qui est souvent plus pratique :
class InvalidAddressException(Exception):
"""Gère les exceptions liées aux mauvaises adresses."""
def __init__(self, address, base_message="Adresse invalide !", *args, **kwargs):
"""Initialise le message.
L’utilisation de *args et **kwags permet de prendre un nombre
de paramètres dynamiques.
"""
msg = f"{base_message} Adresse: {address}"
super().__init__(msg, *args, **kwargs)
Souvenez-vous de l’instruction except – except InvalidAddressException
, par exemple. Le bloc except
correspond au type de l’exception. Autrement dit, le bloc except
aura lieu si l’expression déclenchée est du typeInvalidAddressException
. Le type de l’exception est suffisant à lui seul !
Une dernière chose : les exceptions personnalisées dans une grosse application n’héritent habituellement pas directement d’exception. Nous faisons alors généralement quelque chose qui ressemble à ceci :
class MyAppException(Exception):
pass
class MyAppInvalidAddressException(MyAppException):
pass
Nous définissons MyAppException
comme classe de base pour notre hiérarchie d’exceptions personnalisées. Cela nous permet de savoir très rapidement si une exception est déclenchée depuis notre application ou pas ! Vous verrez que de nombreuses bibliothèques Python ont également leur propre classe de base d’exceptions personnalisées.
Voyons un exemple de comment utiliser les exceptions, dans ce screencast :
À vous de jouer : créez des exceptions personnalisées
Étant donné la classe Utilisateur suivante et son constructeur, créez deux exceptions personnalisées partageant une même classe parent. Ces exceptions personnalisées devront indiquer un nom d’utilisateur trop court ou un mot de passe insuffisant.
Une fois que vous avez vos exceptions personnalisées, modifiez le constructeur ci-dessous pour vérifier que les noms d’utilisateurs comprennent au moins trois caractères, et que les mots de passe contiennent au moins une lettre et un chiffre. Levez l’exception adaptée en cas de problème.
Ensuite, essayez de créer un Utilisateur avec différents noms d’utilisateur et mots de passe, pour tester si cela fonctionne.
class User:
def __init__(self, username, password):
self.username = username
self.password = password
En résumé
Une exception est déclenchée (ou lancée) avec
raise
quand un problème apparaît dans un programme.On peut gérer les exceptions en utilisant des blocs try.
Les exceptions ne sont que des objets, ce qui signifie que nous pouvons définir nos propres exceptions personnalisées !
… et c’est fini ! Mais avant de partir, testez vos connaissances avec notre quiz de troisième partie.