Chapitre 19 : Gestion des Erreurs (try...except)
Exceptions, try, except, finally, raise
1. Quoi : Les Exceptions
Une exception est un événement qui se produit durant l'exécution d'un programme et qui interrompt son flux normal. En Python, les erreurs sont principalement gérées via des exceptions.
Quand une erreur se produit, Python crée un objet exception. Si cet objet n'est pas géré, le programme s'arrête et affiche un "traceback" (la trace de l'erreur).
Exemples d'exceptions courantes :
TypeError: Opération sur un type inapproprié (ex:5 + "hello").ValueError: La valeur d'un argument est incorrecte (ex:int("abc")).NameError: Utilisation d'une variable non définie.IndexError: Accès à un index invalide dans une séquence (ex:my_list[99]).KeyError: Accès à une clé inexistante dans un dictionnaire (ex:my_dict["foo"]).ZeroDivisionError: Division par zéro.
2. Pourquoi : Écrire du code robuste
La gestion des exceptions est cruciale pour créer des programmes robustes et fiables.
- Prévenir les crashs : Au lieu de laisser le programme s'arrêter brutalement, vous pouvez "attraper" l'erreur et la gérer proprement.
- Gérer les cas inattendus : Permet de gérer les erreurs d'entrées utilisateur, les problèmes de réseau, les fichiers manquants, etc.
- Séparer la logique métier du code de gestion d'erreur : Le code "heureux" (le cas où tout se passe bien) est séparé du code qui gère les problèmes.
3. Comment : Le bloc try...except
A. Syntaxe de base
try: Le code susceptible de lever une exception est placé dans le bloctry.except: Si une exception se produit dans le bloctry, Python arrête l'exécution de ce bloc et cherche un blocexceptcorrespondant. Si unexceptcorrespond au type de l'exception, son code est exécuté.
try:
# Code potentiellement dangereux
number = int(input("Enter a number: "))
result = 10 / number
print(f"Result is {result}")
except ValueError:
# Ce bloc s'exécute si int() échoue (ex: l'utilisateur tape "abc")
print("Invalid input. Please enter a valid number.")
except ZeroDivisionError:
# Ce bloc s'exécute si l'utilisateur tape 0
print("You cannot divide by zero.")
B. Attraper plusieurs exceptions
Vous pouvez attraper plusieurs types d'exceptions dans un seul bloc except en les regroupant dans un tuple.
try:
# ...
except (ValueError, ZeroDivisionError):
print("Invalid input or division by zero.")
C. Le bloc except générique
Un bloc except sans type d'exception spécifié attrapera toutes les exceptions. C'est généralement une mauvaise pratique, car cela peut masquer des erreurs inattendues que vous n'aviez pas prévues.
# ❌ Mauvais : Trop large, peut cacher des bugs
try:
# ...
except:
print("An unknown error occurred.")
Il est préférable d'utiliser except Exception as e, qui attrape la plupart des exceptions standards et vous donne accès à l'objet exception.
# ✅ Bon : Attrape la plupart des erreurs et donne des détails
try:
# ...
except Exception as e:
print(f"An error occurred: {e}")
print(f"Error type: {type(e)}")
D. Le bloc else
Le bloc else est optionnel et s'exécute uniquement si aucune exception n'a été levée dans le bloc try. C'est utile pour placer le code qui ne doit s'exécuter que si le bloc try a réussi.
try:
file = open("data.txt", "r")
except FileNotFoundError:
print("File not found.")
else:
# Ce code s'exécute seulement si le fichier a été ouvert avec succès
print("File opened successfully.")
content = file.read()
file.close()
print(f"File content: {content}")
E. Le bloc finally
Le bloc finally est optionnel et s'exécute toujours, qu'une exception ait été levée ou non. C'est l'endroit idéal pour le code de "nettoyage" qui doit absolument être exécuté, comme la fermeture d'un fichier ou d'une connexion réseau.
file = None # On définit la variable avant le try
try:
file = open("data.txt", "r")
# ... opérations sur le fichier
except FileNotFoundError:
print("File not found.")
finally:
# Ce bloc s'exécute dans tous les cas
if file: # On vérifie que le fichier a bien été ouvert
print("Closing the file.")
file.close()
4. Lever une exception (raise)
Vous pouvez aussi déclencher manuellement une exception avec le mot-clé raise. C'est utile pour signaler une erreur dans votre propre code.
def set_age(age):
if age < 0:
# On lève une exception si la condition n'est pas respectée
raise ValueError("Age cannot be negative.")
print(f"Age set to {age}")
try:
set_age(25) # OK
set_age(-5) # Lèvera une ValueError
except ValueError as e:
print(f"Error: {e}")
Exercices :
Exercice 19 - Calculateur de Moyenne Sécurisé
Objectif
Cet exercice a pour but de vous faire utiliser un bloc try...except pour gérer les erreurs potentielles lors du calcul de la moyenne d'une liste de nombres, notamment la division par zéro.
Contexte
Vous devez écrire une fonction qui calcule la moyenne d'une liste de nombres. Cette fonction doit être robuste et ne pas planter si on lui passe une liste vide (ce qui entraînerait une division par zéro).
Énoncé
-
Créez un nouveau fichier Python nommé
safe_average.py. -
Définissez une fonction nommée
calculate_averagequi accepte un seul paramètre,numbers_list.- Ajoutez une docstring pour expliquer ce que fait la fonction et quelles erreurs elle peut gérer.
-
À l'intérieur de la fonction, utilisez un bloc
try...except: a. Dans le bloctry: - Calculez la somme des nombres de la liste avecsum(). - Calculez la longueur de la liste aveclen(). - Calculez la moyenne en divisant la somme par la longueur. - Retournez la moyenne.b. Dans le bloc
except: - Attrapez spécifiquement l'exceptionZeroDivisionError. - Affichez un message d'avertissement indiquant que la liste est vide et que la moyenne ne peut pas être calculée. - Retournez0comme valeur par défaut pour la moyenne d'une liste vide. -
Testez votre fonction :
- Créez deux listes : une avec des nombres et une autre qui est vide.
- Appelez votre fonction
calculate_averagesur chaque liste et affichez le résultat de manière claire.
Résultat Attendu
Calculating average for [10, 20, 30, 40, 50]...
Average: 30.0
Calculating average for []...
Warning: The list is empty, cannot calculate average.
Average: 0
Bonus (Optionnel)
Modifiez votre fonction pour qu'elle gère également le cas où la liste contient des éléments qui ne sont pas des nombres (ce qui lèverait une TypeError avec sum()).
- Ajoutez un autre bloc
except TypeError. - Affichez un message d'erreur approprié et retournez
Nonepour indiquer qu'un calcul est impossible. - Testez ce nouveau cas avec une liste comme
[10, 20, "thirty"].
Résultat Attendu (avec bonus)
Calculating average for [10, 20, 30, 40, 50]...
Average: 30.0
Calculating average for []...
Warning: The list is empty, cannot calculate average.
Average: 0
Calculating average for [10, 20, 'thirty']...
Error: The list contains non-numeric values.
Average: None
Cliquez ici pour voir un exemple de code de solution
# safe_average.py
def calculate_average(numbers_list):
"""
Calculates the average of a list of numbers.
Handles ZeroDivisionError for empty lists and TypeError for non-numeric lists.
Args:
numbers_list (list): A list of numbers.
Returns:
float or int: The average of the numbers.
Returns 0 for an empty list.
Returns None if the list contains non-numeric values.
"""
try:
total = sum(numbers_list)
count = len(numbers_list)
average = total / count
return average
except ZeroDivisionError:
print("Warning: The list is empty, cannot calculate average.")
return 0
except TypeError:
print("Error: The list contains non-numeric values.")
return None
# --- Tests ---
# Test 1: Liste normale
list1 = [10, 20, 30, 40, 50]
print(f"Calculating average for {list1}...")
avg1 = calculate_average(list1)
print(f"Average: {avg1}\n")
# Test 2: Liste vide
list2 = []
print(f"Calculating average for {list2}...")
avg2 = calculate_average(list2)
print(f"Average: {avg2}\n")
# Test 3 (Bonus): Liste avec erreur de type
list3 = [10, 20, "thirty"]
print(f"Calculating average for {list3}...")
avg3 = calculate_average(list3)
print(f"Average: {avg3}\n")