| @@ -0,0 +1,50 @@ | |||||
| Pile d'appels | |||||
| ============= | |||||
| Reprenons un exemple de code qui provoque une erreur, par exemple en essayant | |||||
| de diviser par zéro:: | |||||
| def mauvaise_fonction(): | |||||
| return 1 / 0 | |||||
| def fonction_intermédiaire(): | |||||
| mauvaise_fonction() | |||||
| def fonction_principale(): | |||||
| fonction_intermédiaire() | |||||
| fonction_principale() | |||||
| Si on lance ce code, voilà ce qu'on obtient:: | |||||
| Traceback (most recent call last): | |||||
| File "mauvaises_maths.py", line 13, in <module> | |||||
| fonction_principale() | |||||
| File "mauvaises_maths.py", line 10, in fonction_principale | |||||
| fonction_intermédiaire() | |||||
| File "mauvaises_maths.py", line 6, in fonction_intermédiaire | |||||
| mauvaise_fonction() | |||||
| File "mauvaises_maths.py", line 2, in mauvaise_fonction | |||||
| return 1 / 0 | |||||
| ZeroDivisionError: division by zero | |||||
| Ceci s'appelle une *pile d'appels*. Elle permet de voir exactement par quelles fonction on est passé et | |||||
| dans quel ordre. Elle se lit de haut en bas: | |||||
| * On appelé `fonction_principale()` | |||||
| * Cette fonction a à son tour appelé `fonction_intermédiaire()` | |||||
| * `fonction_intermédiaire()` à appelé `mauvaise_fonction()` | |||||
| * `mauvaise_fonction()` a levé une exception | |||||
| Notez que chaque élément de la pile comprend: | |||||
| * le nom de la fonction | |||||
| * le chemin du module la contetant | |||||
| * le numéro et la ligne précise du code qui a été appelé | |||||
| Il est important de bien lire les piles d'appels quand on cherche | |||||
| à comprendre d'où vient une exception. | |||||
| Après la pile d'appels, on a le *nom* de l'exception et sa *description*. | |||||
| @@ -0,0 +1,75 @@ | |||||
| Exceptions natives | |||||
| ================== | |||||
| Les exceptions sont toujours des instances de classes, et les classes d'exceptions héritent | |||||
| toujours de la class ``BaseException``. | |||||
| Le nom de l'exception est en réalité le nom de la classe, ici l'exception levée par la ligne | |||||
| ``return 1 / 0`` est une instance de la classe ``ZeroDivisionError``. | |||||
| Cette exception fait partie des nombreuses exceptions préféfinies en Python. Ensemble, elles | |||||
| forment une *hiérarchie* dont voici un extrait: | |||||
| .. code-block:: text | |||||
| BaseException | |||||
| +-- SystemExit | |||||
| +-- KeyboardInterrupt | |||||
| +-- Exception | |||||
| +-- ArithmeticError | |||||
| | +-- ZeroDivisionError | |||||
| +-- LookupError | |||||
| | +-- IndexError | |||||
| | +-- KeyError | |||||
| +-- OSError | |||||
| | +-- FileNotFoundError | |||||
| +-- TypeError | |||||
| +-- ValueError | |||||
| IndexError et KeyError | |||||
| ---------------------- | |||||
| ``IndexError`` est levée quand on essaye d'accéder à un index trop grand | |||||
| dans une liste:: | |||||
| ma_liste = ["pomme"] | |||||
| ma_liste[2] = "abricot" | |||||
| # IndexError: list assignment index out of range | |||||
| ``KeyError`` est levée quand on essaye d'accéder à une clé qui n'existe pas | |||||
| dans un dictionnaire:: | |||||
| scores = { "Alice" : 10 } | |||||
| score_de_bob = scores["Bob"] | |||||
| # KeyError: 'Bob' | |||||
| Notez que la description de ``KeyError`` est la valeur de la clé manquante. | |||||
| ValueError | |||||
| ---------- | |||||
| ``ValueError`` est levée (entre autres) quand on tente une maunvaise conversions:: | |||||
| entrée_utilisateur = "pas un nombre" | |||||
| valeu = int(entrée_utilisateur) | |||||
| KeyboardInterrupt | |||||
| ----------------- | |||||
| ``KeyboardInterrupt`` est levée quand on fait ``ctrl-c``. | |||||
| FileNotFoundError | |||||
| ------------------ | |||||
| ``FileNotFoundError`` est levée quand on essaye d'ouvrir | |||||
| en lecture un fichier qui n'exsiste pas:: | |||||
| with open("fichier-inexistant.txt", "r") as f: | |||||
| contenu = f.read() | |||||
| @@ -0,0 +1,101 @@ | |||||
| Gestion des exceptions | |||||
| ====================== | |||||
| Bloc try/except | |||||
| --------------- | |||||
| On peut *gérer* (ou *attraper*) une exception en utilisant un bloc | |||||
| ``try/except`` et le nom d'une classe d'exception:: | |||||
| try: | |||||
| a = 1 / 0 | |||||
| except ZeroDivisionError: | |||||
| print("Ouelqu'un a essayé de diviser par zéro!") | |||||
| # Affiche: Ouelqu'un a essayé de diviser par zéro! | |||||
| À noter : le bloc dans ``try`` s'interrompt dès que l'exception est levée, | |||||
| et on ne passe dans le bloc ``except`` que si une exception a effectivement | |||||
| été levée. | |||||
| .. code-block:: python | |||||
| x = 14 | |||||
| y = 0 | |||||
| try: | |||||
| z = x / y | |||||
| print("z vaut", z) | |||||
| except ZeroDivisionError: | |||||
| print("Ouelqu'un a essayé de diviser par zéro!") | |||||
| # Affiche: Ouelqu'un a essayé de diviser par zéro! | |||||
| Notez que la ligne ``print("z vaut", z)`` n'as pas été exécutée. | |||||
| Autr exemple: | |||||
| .. code-block:: python | |||||
| x = 14 | |||||
| y = 2 | |||||
| try: | |||||
| z = x / y | |||||
| print("z vaut", z) | |||||
| except ZeroDivisionError: | |||||
| print("Ouelqu'un a essayé de diviser par zéro!") | |||||
| # Affiche: 'z vaut 7.0' | |||||
| Notez que la ligne ``print("Ouelqu'un a essayé de diviser par zéro!")`` n'as pas été exécutée. | |||||
| Gestion de plusieurs exceptions | |||||
| -------------------------------- | |||||
| Le mot après ``except`` doit être celui d'une classe, et l'exception n'est gérée | |||||
| que si sa classe est **égale ou une fille** de celle ci. | |||||
| Par exemple, ceci fonctionne car ``ZeroDivisionError`` est bien une fille | |||||
| de la classe ``ArithmeticError``:: | |||||
| x = 14 | |||||
| y = 0 | |||||
| try: | |||||
| z = x / y | |||||
| print("z vaut", z) | |||||
| except ArithmeticError: | |||||
| print("Ouelqu'un a essayé une opération impossible") | |||||
| On peut aussi mettre plusieurs blocs de ``except``:: | |||||
| try: | |||||
| tente_un_truc_risqué() | |||||
| except ZeroDivisionError: | |||||
| print("raté : division par zéro!") | |||||
| except FileNotFoundError: | |||||
| print("raté : fichier non trouvé") | |||||
| Ou gérer des exception de classes différentes avec le même bloc:: | |||||
| try: | |||||
| tente_un_truc_risqué() | |||||
| except (ZeroDivisionError, FileNotFoundError) | |||||
| print("raté!") | |||||
| Accéder à la valeur de l'exception | |||||
| ----------------------------------- | |||||
| On peut récupérer l'instance de l'exception levée avec ``as``:: | |||||
| try: | |||||
| ounver_fichier() | |||||
| except FileNotFoundError as e: | |||||
| print("le fichier: ", e.filename, "n'existe pa") | |||||
| Ici on utilise l'attribut ``filename`` de la classe ``FileNotFoundError`` | |||||
| pour afficher un message d'erreur | |||||
| @@ -0,0 +1,54 @@ | |||||
| Levée d'exceptions | |||||
| ================== | |||||
| raise | |||||
| ----- | |||||
| On peut lever explicitement un exception en appelant le mot-clé ``raise`` suivi | |||||
| d'une **instance** d'une classe. | |||||
| Par exemple en utilisant une exception native:: | |||||
| def dire_bonjour(prénom): | |||||
| if not prénom: | |||||
| raise ValueError("prénom vide") | |||||
| Définition d'exceptions à la carte | |||||
| ----------------------------------- | |||||
| On peut ré-utiliser les exceptions natives, ou définir sa propre classe:: | |||||
| class OpérationImpossible(Exception): | |||||
| pass | |||||
| def ma_fonction(): | |||||
| if cas_impossible: | |||||
| raise OpérationImpossible() | |||||
| Gérer puis re-lever l'exception géré | |||||
| ------------------------------------- | |||||
| Parfois il est utile de re-lever l'exception qu'on vient de géner. | |||||
| Dans ce cas, on utilise ``raise`` sans argument:: | |||||
| try: | |||||
| tente_un_truc_risqué() | |||||
| exeept ArithmeticError: | |||||
| ... | |||||
| raise | |||||
| raise from | |||||
| ---------- | |||||
| On peut donner une *cause directe* lorsqu'on lève un exception avec ``from``:: | |||||
| def appelle_maman(): | |||||
| numéro = répertoire["Maman"] | |||||
| try: | |||||
| appelle_maman() | |||||
| except KeyError as e: | |||||
| raise AppelImpossible from e | |||||
| @@ -0,0 +1,37 @@ | |||||
| Else et finally | |||||
| =============== | |||||
| else | |||||
| ---- | |||||
| Si on rajoute un bloc ``else`` après le ``except``, le bloc n'est éxécuté que si | |||||
| *aucune* exception n'a été levée:: | |||||
| try: | |||||
| tente_un_truc_risqué() | |||||
| except (ZeroDivisionError, FileNotFoundError): | |||||
| print("raté") | |||||
| else: | |||||
| print("ouf - ça a marché") | |||||
| finally | |||||
| -------- | |||||
| Si on rajoute un bloc ``finally`` après le ``except``, le bloc est éxécuté *dans tous les cas*, | |||||
| qu'une exception ait été levée ou non. On s'en sert souvent pour "annuler" du code | |||||
| qui aurait été utilisé dans le bloc ``try``:: | |||||
| personnage = Personnage() | |||||
| try: | |||||
| personnage.entre_en_scène() | |||||
| personnage.tente_un_truc_risqué() | |||||
| except ZeroDivisionError: | |||||
| print("raté") | |||||
| finally: | |||||
| personnage.quitte_la_scène() | |||||
| Si dans le bloc ``try`` une exception **différente** de ``ZeroDivisionError`` est | |||||
| levée, on passera *quand même* dans le bloc ``finally``, *puis* l'exception sera | |||||
| levée à nouveau. | |||||
| @@ -0,0 +1,16 @@ | |||||
| Chapitre 19 - Exceptions | |||||
| ======================== | |||||
| On a souvent montré des exemples de code qui provoquaient des erreurs au | |||||
| cours des chapitres précédents. Il est temps maintenant de se pencher | |||||
| davantage sur celles-ci. | |||||
| .. toctree:: | |||||
| :maxdepth: 1 | |||||
| ./01-piles.rst | |||||
| ./02-exceptions-natives.rst | |||||
| ./03-gestion.rst | |||||
| ./04-levée.rst | |||||
| ./05-else-finally.rst | |||||
| @@ -0,0 +1,18 @@ | |||||
| class Personnage: | |||||
| def tente_un_truc_risqué(self): | |||||
| 1 / 0 | |||||
| def quitte_la_scène(self): | |||||
| print("je m'en vais") | |||||
| def entre_en_scène(): | |||||
| return Personnage() | |||||
| personnage = entre_en_scène() | |||||
| try: | |||||
| personnage.tente_un_truc_risqué() | |||||
| except FileNotFoundError: | |||||
| print("raté") | |||||
| personnage.quitte_la_scène() | |||||
| @@ -35,3 +35,4 @@ remarques. | |||||
| 16-interpréteur-interactif/index | 16-interpréteur-interactif/index | ||||
| 17-classes-03/index | 17-classes-03/index | ||||
| 18-functions-02/index | 18-functions-02/index | ||||
| 19-exceptions/index | |||||