% Programmation avec Python (chapitre 10) % Dimitri Merejkowsky # \center \huge Exceptions # Exemple ```python def une_fonction(): return 1 / 0 def une_autre_fonction(): une_fonction() une_autre_fonction() ``` # Les backtraces ``` Traceback (most recent call last): File "foo.py", line 7, in une_autre_fonction() File "foo.py", line 5, in une_autre_fonction une_fonction() File "foo.py", line 2, in une_fonction return 1 / 0 ZeroDivisionError: division by zero ``` ZeroDivisionError est une *classe*. # Exemple modifié ```python def une_fonction(diviseur): return 1 / diviseur def une_autre_fonction(): une_fonction(diviseur=0) une_autre_fonction() ``` # Exemple modifié ``` Traceback (most recent call last): File "foo.py", line 7, in une_autre_fonction() File "foo.py", line 5, in une_autre_fonction une_fonction(diviseur=0) File "foo.py", line 2, in une_fonction return 1 / diviseur ZeroDivisionError: division by zero ``` **Prenez le temps de lire les backtraces soigneusement!** # Exemples d'erreurs * `b += 2` - **NameError** * `a / 0` - **ZeroDivisionError** * `my_list[42]` - **IndexError** * ``my_dict["bad-key"]`` - **KeyError** * `1 + "two"` - **TypeError** * `open("badpath")` - **FileNotFoundError** # Hiérarchie d'exceptions (simplifiée) ``` BaseException +-- SystemExit +-- KeyboardInterrupt +-- Exception +-- ArithmeticError | +-- ZeroDivisionError +-- LookupError | +-- IndexError | +-- KeyError +-- OSError | +-- FileNotFoundError +-- TypeError +-- ValueError ``` Liste complète dans la documentation: https://docs.python.org/fr/3/library/exceptions.html#exception-hierarchy # KeyboardInterrupt et SystemExit * `KeyboardInterrupt` est levée quand on fait `ctrl-c`. * `SystemExit` est levé quand on utilise `sys.exit()` * Python cache la backtrace dans ce cas-là * Pas utile de l'attrapper en général # Attraper une exception ```python try: a = 1 / 0 this_will_never_happen() except ZeroDivisionError: print("someone tried to divide by zero!") ``` * Note: si l'exception n'est pas une fille de la classe attrapée, c'est raté. # Attraper une exception On peut mettre plusieurs blocs de `except` ```python try: something_dangerous() except ZeroDivisionError: print("tried to devide by zero") except FileNotFoundError: print("file not found") ``` # Attraper une exception On peut attraper plusieurs exceptions d'un coup ```python try: something_dangerous() except (ZeroDivisionError, FileNotFoundError): print("something bad happened") ``` # finally, else ```python try: something_dangerous() except SomeError: print("something bad happened") else: print("everything went well") finally: clean_up() ``` # WBYL * Watch Before You Leap ```python if exists(): if pas_un_dossier(): if j_ai_les_droits_en_lecture(): open(filename): ``` # EAFP * Easier to Ask for Forgiveness than Permission ```python try: file = open(filename) except IOError: ... finally: file.close() ``` # Avec with Pas besoin de `finally` :) ```python try: with open(filename) as file: lines = file.readlines() except FileNotFoundError: print("Fichier non trouvé") ``` L'exception est capturée, le fichier est fermé, puis l'exception est levée à nouveau. # Attention à la position du except ```python if truc: if machin: for bidule in chose: raise MyError("kaboom!") ```` # Attention à la position du except ```python if truc: if machin: try: for bidule in chose: raise MyError("kaboom!") except: ... ``` # Attention à la position du except ```python if truc: try: if machin: for bidule in chose: raise MyError("kaboom!") except: ... ```` # Accédér aux détails de l'exception * Avec `as`: ```python try: something_dangerous() except FileNotFoundError as error: print("file not found:", error.filename) ``` \vfill Voir la documentation pour les attributs disponibles. # Lever une exception * Avec raise ```python def retirer_somme(compte, montant): solde = ... if montant >= solde: raise ValueError("montant supérieur au solde!") ``` # Créer vos propres exceptions Toujours hériter de `Exception`. ```python class BankError(Exception): .... class NotEnoughMoney(BankError): def __init__(self, amount, withdrawal): self.amount = amount self.withdrawal = withdrawal ``` # \center \huge Atelier # Convertisseur d'unités Miles en kilomètres, gallons en litres, etc. # \center \huge Pour la prochain fois # Consignes Point de départ; `git.e2li.org / dmerejkowsky / cours-python / sources / conversions` * Rajouter le support d'autres unités (comme les volumes, ...) * Gérer toutes les erreurs possibles (unité non trouvée, ...) Plus dur: * Gérer les vitesses (mètres par secone vers miles par heure) * Gérer les températures (dégres Celsius vers Farenheit) * Gérer les monnaies # Contact Envoyez questions, résultats de vos travaux ou resources sur Python à `d.merej@gmail.com`.