Updated first chapter slides. Added new questions in the new training section.
164 lines
5.3 KiB
Markdown
164 lines
5.3 KiB
Markdown
---
|
||
title: Le droit aux erreurs
|
||
author: Steve Kossouho
|
||
---
|
||
|
||
# Le droit aux erreurs
|
||
|
||
----
|
||
|
||
## Exceptions
|
||
|
||
En Python, une [exception]{.naming} est une erreur qui se produit pendant l'exécution de votre code.
|
||
En tant que développeur, vous pouvez **intercepter** une exception, et dire à Python ce que vous souhaitez faire
|
||
afin d'empêcher le programme de se terminer prématurément si ce n'est pas nécessaire.
|
||
|
||
----
|
||
|
||
### Jargon des exceptions
|
||
|
||
- [Lever une exception]{.naming} : Ce que Python effectue lorsqu'on rencontre une erreur à l'exécution d'un script.
|
||
- [Traceback]{.naming} : <span style="color:#F04">Texte qui apparaît</span> dans la sortie d'erreur (en rouge) lorsque votre programme plante à l'exécution. C'est un récapitulatif des exécutions d'instructions qui ont mené directement à l'interruption de l'exécution, accompagné d'informations sur l'exception.
|
||
|
||
Les exceptions levées par Python sont d'un type qui dépend de l'erreur rencontrée (ex. `TypeError`{.python}).
|
||
Tous ces types font partie d'une hiérarchie dont le type le plus générique est nommé `Exception`.
|
||
Théoriquement, un développeur peut créer son propre type d'exception lorsqu'il développe des bibliothèques.
|
||
|
||
----
|
||
|
||
### Exemple de gestion d'exception
|
||
|
||
Dans l'exemple ci-dessous, on effectue une division par zéro, ce qui est toujours une erreur, mais on pourrait accéder
|
||
à un mauvais index de liste, à une clé de dictionnaire inexistante, ou encore à un fichier verrouillé…
|
||
|
||
```python {.numberLines}
|
||
try:
|
||
resultat = 15 / 0 # erreur évidente pour l'exemple
|
||
print("Cette ligne ne sera jamais exécutée.")
|
||
except ZeroDivisionError: # Ici, on intercepte spécifiquement le type d'erreur produit par la division
|
||
print("Le résultat n'a pas pu être calculé.")
|
||
|
||
print("Fin du programme.")
|
||
```
|
||
|
||
Gestion des erreurs de division par zéro
|
||
|
||
----
|
||
|
||

|
||
|
||
----
|
||
|
||
### Mauvaise pratique sur les exceptions
|
||
|
||
Il est **ardemment** déconseillé d'utiliser directement la classe `Exception`{.python} (ou ne rien préciser, d'ailleurs) avec la clause `except`{.python};
|
||
une clause trop générique intercepte **tous** les problèmes rencontrés dans le bloc `try`{.python}.
|
||
Dans certains cas, cela vous fera manquer des erreurs que vous ne souhaitez pas traiter avec le même bloc `except`{.python}.
|
||
|
||
Une règle d'or de Python indique que dans le cas où vous masquez tous les types d'exception sans distinction, vous
|
||
ne devez jamais le faire silencieusement; affichez le problème ou consignez-le quelque part :
|
||
|
||
```python {.numberLines}
|
||
import traceback
|
||
|
||
try:
|
||
print(15 / 0)
|
||
except Exception: # ou except:
|
||
traceback.print_exc() # Affiche le traceback dans la console sans planter
|
||
```
|
||
|
||
----
|
||
|
||
### Un bloc `except`, plusieurs exceptions
|
||
|
||
On peut même avoir des blocs `except`{.python} prenant en charge plusieurs types d'exceptions, pour cela il suffit d'indiquer
|
||
un tuple de classes d'exceptions à la clause `except`{.python} :
|
||
|
||
```python {.numberLines}
|
||
try:
|
||
pass # ou faire autre chose
|
||
except (TypeError, ValueError):
|
||
pass
|
||
```
|
||
|
||
----
|
||
|
||
### Un bloc `try`, plusieurs gestions
|
||
|
||
On peut, à l'instar de la structure `if/elif/else`, faire suivre le bloc `try`{.python} de
|
||
plusieurs blocs `except`{.python} :
|
||
|
||
```python {.numberLines}
|
||
try:
|
||
print("Code à essayer")
|
||
except ValueError:
|
||
print("Code pour ValueError")
|
||
except TypeError:
|
||
print("Code pour TypeError")
|
||
```
|
||
|
||
Python évaluera la structure de haut en bas. Si une erreur se produit dans le bloc `try`{.python},
|
||
Python évaluera d'abord le premier bloc `except`{.python}, de la même manière que pour toutes les structures de code.
|
||
|
||
----
|
||
|
||
Si aucun des blocs `except`{.python} ne gère précisément l'erreur qui se produit dans le bloc `try`{.python},
|
||
alors l'interpréteur Python plante avec un traceback dans la console, comme attendu.
|
||
|
||
```python {.numberLines}
|
||
try:
|
||
1 / 0
|
||
except TypeError: # pas la bonne exception
|
||
pass
|
||
except NameError: # pas la bonne exception non plus
|
||
pass
|
||
```
|
||
|
||
----
|
||
|
||
### Code exécuté si tout se passe bien
|
||
|
||
La structure `try`{.python}...`except`{.python} peut être accompagnée d'un bloc `else`{.python}. Ce bloc
|
||
sera exécuté si aucun des blocs `except`{.python} précédents n'a été exécuté (comme dans une structure `if`{.python}) :
|
||
|
||
```python {.numberLines}
|
||
try:
|
||
value = input("Saisissez un nombre entier : ")
|
||
value = int(value)
|
||
except ValueError:
|
||
value = None
|
||
else:
|
||
# Exécuté si le bloc try n'est pas interrompu par une erreur
|
||
print(f"Merci, la valeur est de {value}.")
|
||
```
|
||
|
||
----
|
||
|
||
### Exécution garantie
|
||
|
||
Pour finir, il existe un bloc `finally`{.python}, dont le bloc est *toujours* exécuté, même si le programme doit planter :
|
||
|
||
```python {.numberLines}
|
||
try:
|
||
15 / 0
|
||
except KeyError:
|
||
pass
|
||
finally:
|
||
print("Toujours exécuté")
|
||
```
|
||
|
||
En général, on imagine l'utiliser lorsqu'on souhaite penser à toujours libérer une ressource (connexion, fichier etc.)
|
||
|
||
----
|
||
|
||
### Lever une exception
|
||
|
||
On peut souhaiter également lever soi-même une exception pour notifier un problème à gérer.
|
||
C'est généralement le cas lorsque l'on développe une bibliothèque utilisable par d'autres développeurs.
|
||
|
||
```python {.numberLines}
|
||
def do_something(value):
|
||
if not isinstance(value, int): # si value n'est pas un entier
|
||
raise TypeError("Value must be an integer.")
|
||
```
|