Outils pour utilisateurs

Outils du site


cours:informatique:dev:python:programmation_orientee_objet:330_gestion_des_exceptions

Notes et transcriptions du cours “Apprenez la programmation orientée objet avec Python” disponible sur la plateforme Openclassrooms.

Gérez les exceptions

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 toutes héritent. 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 un try-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 (bubbling). 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.

Si dans cet exemple nous avons soulevé Exception comme erreur par défaut, c’est globalement une mauvaise pratique ! Utilisez toujours des erreurs plus ciblées pour être sûr de ne pas passer sous silence des erreurs qui pourraient générer des résultats inattendus dans votre code. 📛

É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 type InvalidAddressException. 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.

Plus l'erreur est explicite, plus il sera aisé de la corriger.

À 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

Proposition

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

◁ Précédent | ⌂ Retour au sommaire | Suivant ▷

cours/informatique/dev/python/programmation_orientee_objet/330_gestion_des_exceptions.txt · Dernière modification : 2023/11/04 16:45 de yoann