La gestion des erreurs
Introduction
Comme en C#, Python utilise des exceptions pour signaler les erreurs rencontrées pendant l'exécution d'un programme. Une exception interrompt le déroulement lors de l'exécution du code, et se propage jusqu'à un gestionnaire capable de la traiter. Si elle n'est pas interceptée, l'exécution s'interrompt et Python affiche les informations concernant l'erreur rencontrée.
D'un point de vue terminologie, on dit lever une exception pour la déclencher et d'intercepter une exception pour la traiter.
Intercepter une exception
En Python, les mots-clés try et except correspondent à try et catch en C# :
text = "abc"
try:
number = int(text)
print(number)
except ValueError as error:
print(f"Conversion impossible : {error}")
Le bloc try contient les instructions susceptibles de lever une exception. Le bloc except précise le type d'exception à intercepter et le traitement à exécuter. En accord avec les bonnes pratiques, il est préférable d'intercepter des types d'exceptions précis. Un bloc except: sans type intercepte également les exceptions liées à l'interruption de l'exécution du code, comme KeyboardInterrupt, et cela doit être évité.
Dans cet exemple, la conversion échoue et lève une exception de type ValueError. as error permet d'accéder à l'objet exception et est facultatif si cet objet n'est pas utilisé.
Voici l'équivalent en C# :
string text = "abc";
try
{
int number = int.Parse(text);
Console.WriteLine(number);
}
catch (FormatException error)
{
Console.WriteLine($"Conversion impossible : {error.Message}");
}
Intercepter plusieurs types d'exceptions
Plusieurs blocs except peuvent traiter différemment les exceptions :
try:
number = int(input("Saisissez un entier : "))
result = 100 / number
print(result)
except ValueError:
print("La valeur saisie n'est pas un entier.")
except ZeroDivisionError:
print("La division par zéro est impossible.")
Un seul bloc except est exécuté : le premier dont le type correspond à l'exception levée. Les types les plus spécifiques doivent donc apparaître avant leurs classes parentes.
Pour appliquer le même traitement à plusieurs types, nous les regroupons dans un tuple :
Voici quelques exceptions couramment utilisées :
| Exception | Signification |
|---|---|
ValueError |
Une valeur est inappropriée pour l'opération demandée. |
TypeError |
Un objet possède un type incompatible avec l'opération demandée. |
ZeroDivisionError |
Une division par zéro est effectuée. |
IndexError |
Un indice se trouve en dehors des limites d'une séquence. |
KeyError |
Une clé est absente d'un dictionnaire. |
FileNotFoundError |
Un fichier ou un répertoire demandé est introuvable. |
Exécuter du code en cas de réussite
Python propose un bloc else, exécuté lorsque le bloc try se termine normalement, sans exception ni sortie anticipée par une instruction return, break ou continue.
try:
number = int("42")
except ValueError:
print("Conversion impossible.")
else:
print(f"Conversion réussie : {number}")
Il permet de distinguer le traitement susceptible d'échouer du traitement à effectuer après sa réussite. Une exception levée dans le bloc else n'est pas interceptée par les blocs except associés à ce try.
Exécuter un traitement de "finalisation"
Comme en C#, le bloc finally permet d'exécuter un traitement à la sortie du bloc try, qu'une exception ait été levée ou non. Il s'exécute également lorsqu'une instruction return met fin à l'exécution de la fonction.
try:
number = int("abc")
except ValueError:
print("Conversion impossible.")
finally:
print("Fin du traitement.")
Pour les ressources telles que les fichiers, il existe l'instruction with, comparable à using en C# :
try:
with open("data.txt", encoding="utf-8") as file:
content = file.read()
except OSError as error:
print(f"Lecture impossible : {error}")
Le fichier est automatiquement fermé à la sortie du bloc with, même si une exception survient pendant sa lecture.
Lever une exception
Le mot-clé raise correspond à throw en C#. Il permet de signaler explicitement une erreur :
def calculate_total(price: float, quantity: int) -> float:
if price < 0:
raise ValueError("Le prix ne peut pas être négatif.")
if quantity <= 0:
raise ValueError("La quantité doit être strictement positive.")
return price * quantity
L'appelant peut intercepter cette exception :
Relancer une exception
Dans un bloc except, l'instruction raise sans argument relance l'exception en cours. Elle correspond à l'utilisation de throw; en C# et préserve les informations d'origine sur l'erreur rencontrée.
Ce mécanisme permet de loguer une erreur avant qu'elle remonte à l'appelant.
Implémenter une exception personnalisée
Une exception personnalisée est une classe qui hérite de Exception :
Le mot-clé pass indique ici que la classe n'ajoute aucun comportement à celui de sa classe parente.
def check_stock(stock: int, quantity: int) -> None:
if quantity > stock:
raise InsufficientStockError(
"Le stock disponible est insuffisant."
)
Cette exception peut ensuite être interceptée par son type :
En Python, les noms d'exceptions se terminent généralement par Error, tandis que le suffixe Exception est habituel en C#.
Conserver la cause d'une exception
Lorsqu'une erreur technique est remplacée par une exception plus adaptée au contexte, la syntaxe raise ... from ... permet d'indiquer la cause de manière explicite :
try:
quantity = int("abc")
except ValueError as error:
raise ValueError("La quantité saisie est invalide.") from error
Ce mécanisme est comparable à l'utilisation de InnerException en C#. La cause est accessible avec l'attribut __cause__.
Récapitulatif des correspondances entre Python et C#
| C# | Python |
|---|---|
try |
try |
catch |
except |
finally |
finally |
throw new ... |
raise ... |
throw; |
raise sans argument |
using |
with pour les gestionnaires de contexte |
InnerException |
__cause__ avec raise ... from ... |
catch (Exception ex) |
except Exception as error |