Skip to main content
Niveau : Intermédiaire

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 bloc try.
  • except : Si une exception se produit dans le bloc try, Python arrête l'exécution de ce bloc et cherche un bloc except correspondant. Si un except correspond 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é

  1. Créez un nouveau fichier Python nommé safe_average.py.

  2. Définissez une fonction nommée calculate_average qui accepte un seul paramètre, numbers_list.

    • Ajoutez une docstring pour expliquer ce que fait la fonction et quelles erreurs elle peut gérer.
  3. À l'intérieur de la fonction, utilisez un bloc try...except : a. Dans le bloc try : - Calculez la somme des nombres de la liste avec sum(). - Calculez la longueur de la liste avec len(). - Calculez la moyenne en divisant la somme par la longueur. - Retournez la moyenne.

    b. Dans le bloc except : - Attrapez spécifiquement l'exception ZeroDivisionError. - Affichez un message d'avertissement indiquant que la liste est vide et que la moyenne ne peut pas être calculée. - Retournez 0 comme valeur par défaut pour la moyenne d'une liste vide.

  4. Testez votre fonction :

    • Créez deux listes : une avec des nombres et une autre qui est vide.
    • Appelez votre fonction calculate_average sur 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 None pour 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")