Skip to content
Thèmes
Python
Python Try Except: How to Handle Exceptions the Right Way

Python Try Except : Comment gérer les exceptions correctement

Updated on

Votre script Python lit un fichier, analyse les données et les envoie à une API. Il fonctionne parfaitement sur votre machine. Puis il s'exécute sur un serveur où le chemin du fichier est incorrect, le JSON est mal formé ou le réseau est en panne. Le programme plante avec un traceback et tout le pipeline s'arrête. C'est le problème que try/except résout. Au lieu de laisser les erreurs tuer votre programme, vous les interceptez, les gérez et continuez l'exécution.

📚

Qu'est-ce qu'une exception en Python ?

Une exception est un événement qui perturbe le flux normal d'un programme. Quand Python rencontre une opération qu'il ne peut pas effectuer -- division par zéro, accès à une clé de dictionnaire manquante, ouverture d'un fichier inexistant -- il crée un objet exception et arrête l'exécution. Si rien n'intercepte cette exception, le programme se termine et affiche un traceback.

# This crashes the program
result = 10 / 0

Sortie :

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    result = 10 / 0
ZeroDivisionError: division by zero

Les exceptions sont différentes des erreurs de syntaxe. Une erreur de syntaxe signifie que Python ne peut pas du tout analyser votre code. Une exception se produit pendant l'exécution, après que le code a été analysé avec succès.

Syntaxe de base Try/Except

Le bloc try/except vous permet de tenter du code qui pourrait échouer et de définir ce qui se passe en cas d'échec.

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero.")

Sortie :

Cannot divide by zero.

Le programme ne plante pas. Python exécute le code à l'intérieur de try. Quand ZeroDivisionError se produit, l'exécution saute au bloc except. Tout ce qui suit le try/except continue normalement.

Vous pouvez également capturer l'objet exception pour inspecter le message d'erreur :

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"Error: {e}")

Sortie :

Error: division by zero

Intercepter des exceptions spécifiques

Python possède des dizaines de types d'exceptions intégrées. Intercepter le bon type rend votre code précis. Voici les exceptions les plus courantes que vous gérerez.

ValueError

Levée quand une fonction reçoit un argument du bon type mais avec une valeur inappropriée.

try:
    number = int("not_a_number")
except ValueError as e:
    print(f"Invalid value: {e}")

Sortie :

Invalid value: invalid literal for int() with base 10: 'not_a_number'

TypeError

Levée quand une opération est appliquée à un objet du mauvais type.

try:
    result = "hello" + 42
except TypeError as e:
    print(f"Type error: {e}")

Sortie :

Type error: can only concatenate str (not "int") to str

FileNotFoundError

Levée quand vous essayez d'ouvrir un fichier qui n'existe pas.

try:
    with open("nonexistent_file.txt", "r") as f:
        content = f.read()
except FileNotFoundError:
    print("File not found. Check the file path.")

KeyError

Levée quand vous accédez à une clé de dictionnaire qui n'existe pas.

data = {"name": "Alice", "age": 30}
 
try:
    email = data["email"]
except KeyError as e:
    print(f"Missing key: {e}")

Sortie :

Missing key: 'email'

IndexError

Levée quand vous accédez à un index de liste hors limites.

items = [10, 20, 30]
 
try:
    value = items[5]
except IndexError:
    print("Index out of range.")

Blocs Except multiples

Vous pouvez gérer différents types d'exceptions avec des blocs except séparés. Python les vérifie dans l'ordre et exécute la première correspondance.

def parse_config(raw_value):
    try:
        parts = raw_value.split(":")
        key = parts[0]
        value = int(parts[1])
        return {key: value}
    except IndexError:
        print("Config format error: missing colon separator.")
    except ValueError:
        print("Config format error: value is not a number.")
    except AttributeError:
        print("Config format error: input is not a string.")
 
parse_config("timeout:30")    # Returns {'timeout': 30}
parse_config("timeout")       # Config format error: missing colon separator.
parse_config("timeout:abc")   # Config format error: value is not a number.
parse_config(12345)            # Config format error: input is not a string.

Vous pouvez également intercepter plusieurs exceptions dans un seul bloc except en utilisant un tuple :

try:
    value = int(input("Enter a number: "))
    result = 100 / value
except (ValueError, ZeroDivisionError) as e:
    print(f"Invalid input: {e}")

Le bloc Else

Le bloc else s'exécute uniquement quand aucune exception ne se produit dans le bloc try. Il sépare le code du "chemin heureux" du code de gestion des erreurs.

try:
    number = int("42")
except ValueError:
    print("That is not a valid number.")
else:
    print(f"Successfully parsed: {number}")

Sortie :

Successfully parsed: 42

Le bloc else est utile car toute exception levée à l'intérieur n'est pas interceptée par les blocs except précédents. Cela empêche de masquer accidentellement des bugs dans votre code de chemin de succès.

filename = "data.txt"
 
try:
    f = open(filename, "r")
except FileNotFoundError:
    print(f"File '{filename}' does not exist.")
else:
    content = f.read()
    f.close()
    print(f"Read {len(content)} characters.")

Si open() échoue, le bloc except le gère. Si open() réussit, le bloc else lit le fichier. Toute erreur pendant f.read() n'est pas interceptée ici -- elle se propage vers le haut, ce qui est le comportement correct.

Le bloc Finally

Le bloc finally s'exécute toujours, qu'une exception se soit produite ou non. C'est l'endroit approprié pour le code de nettoyage : fermer des fichiers, libérer des verrous, se déconnecter des bases de données.

f = None
try:
    f = open("data.txt", "r")
    content = f.read()
except FileNotFoundError:
    print("File not found.")
finally:
    if f is not None:
        f.close()
        print("File handle closed.")

Le bloc finally s'exécute même si le bloc try retourne une valeur ou lève une exception non gérée.

def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None
    finally:
        print("Division attempted.")
 
result = divide(10, 3)
# Output: Division attempted.
# result = 3.3333333333333335
 
result = divide(10, 0)
# Output: Division attempted.
# result = None

Le patron complet Try/Except/Else/Finally

Voici comment les quatre blocs fonctionnent ensemble :

import json
 
def load_config(filepath):
    """Load and parse a JSON config file."""
    f = None
    try:
        f = open(filepath, "r")
        data = json.load(f)
    except FileNotFoundError:
        print(f"Config file '{filepath}' not found.")
        return {}
    except json.JSONDecodeError as e:
        print(f"Invalid JSON in '{filepath}': {e}")
        return {}
    else:
        print(f"Config loaded successfully with {len(data)} keys.")
        return data
    finally:
        if f is not None:
            f.close()
            print("File handle released.")
 
config = load_config("settings.json")
BlocQuand il s'exécuteObjectif
tryToujours (en premier)Contient le code qui pourrait lever une exception
exceptSeulement quand une exception correspondante se produitGère l'erreur
elseSeulement quand aucune exception ne se produit dans tryExécute la logique de succès
finallyToujours (en dernier)Code de nettoyage qui doit s'exécuter quoi qu'il arrive

Référence des exceptions intégrées courantes

Python inclut une hiérarchie d'exceptions intégrées. Voici celles que vous rencontrerez le plus souvent.

ExceptionQuand elle se produitExemple
ExceptionClasse de base pour la plupart des exceptionsParent de toutes les exceptions non-system-exiting
ValueErrorMauvaise valeur pour le type correctint("abc")
TypeErrorMauvais type pour une opération"text" + 5
KeyErrorClé de dictionnaire manquanted["missing"]
IndexErrorIndex de liste hors limites[1,2,3][10]
FileNotFoundErrorLe fichier n'existe pasopen("no.txt")
ZeroDivisionErrorDivision ou modulo par zéro1 / 0
AttributeErrorL'objet n'a pas l'attributNone.append(1)
ImportErrorL'importation du module échoueimport nonexistent
OSErrorL'opération OS échoueDisque plein, permission refusée
StopIterationL'itérateur n'a plus d'élémentsnext() sur itérateur épuisé
RuntimeErrorErreur d'exécution génériqueFourre-tout pour les échecs divers

Toutes ces exceptions héritent de BaseException. En pratique, vous devriez intercepter Exception ou ses sous-classes, jamais directement BaseException (qui inclut SystemExit et KeyboardInterrupt).

Lever des exceptions avec raise

Vous pouvez lever des exceptions explicitement quand votre code détecte des conditions invalides. C'est ainsi que vous imposez des préconditions et signalez des erreurs à l'appelant.

def set_age(age):
    if not isinstance(age, int):
        raise TypeError(f"Age must be an integer, got {type(age).__name__}")
    if age < 0 or age > 150:
        raise ValueError(f"Age must be between 0 and 150, got {age}")
    return age
 
# Valid usage
print(set_age(25))  # 25
 
# Invalid usage
try:
    set_age(-5)
except ValueError as e:
    print(e)  # Age must be between 0 and 150, got -5
 
try:
    set_age("thirty")
except TypeError as e:
    print(e)  # Age must be an integer, got str

Vous pouvez également relever l'exception actuelle à l'intérieur d'un bloc except en utilisant raise sans arguments. Cela est utile quand vous voulez enregistrer une erreur puis la laisser se propager.

import logging
 
try:
    result = 10 / 0
except ZeroDivisionError:
    logging.error("Division by zero encountered")
    raise  # re-raises the original ZeroDivisionError

Classes d'exceptions personnalisées

Pour les projets plus importants, définissez vos propres classes d'exceptions. Les exceptions personnalisées rendent la gestion des erreurs plus lisible et permettent aux appelants d'intercepter des modes de défaillance spécifiques.

class ValidationError(Exception):
    """Raised when input data fails validation."""
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f"Validation failed on '{field}': {message}")
 
class DatabaseConnectionError(Exception):
    """Raised when the database connection fails."""
    pass
 
# Usage
def validate_email(email):
    if "@" not in email:
        raise ValidationError("email", "Must contain @ symbol")
    if "." not in email.split("@")[1]:
        raise ValidationError("email", "Domain must contain a dot")
    return email
 
try:
    validate_email("userexample.com")
except ValidationError as e:
    print(e)          # Validation failed on 'email': Must contain @ symbol
    print(e.field)    # email
    print(e.message)  # Must contain @ symbol

Les exceptions personnalisées doivent hériter de Exception, pas de BaseException. Regroupez les exceptions liées sous une classe de base commune pour permettre aux appelants d'intercepter des catégories larges ou étroites :

class AppError(Exception):
    """Base exception for this application."""
    pass
 
class ConfigError(AppError):
    pass
 
class NetworkError(AppError):
    pass
 
# Caller can catch all app errors or specific ones
try:
    raise NetworkError("Connection timed out")
except AppError as e:
    print(f"Application error: {e}")

Bonnes pratiques pour la gestion des exceptions Python

1. N'interceptez jamais des exceptions génériques

Un except: générique intercepte tout, y compris KeyboardInterrupt et SystemExit. Cela masque les bugs et rend le débogage cauchemardesque.

# BAD - catches everything, hides real bugs
try:
    do_something()
except:
    pass
 
# GOOD - catches specific exceptions
try:
    do_something()
except ValueError as e:
    logging.warning(f"Invalid value: {e}")

Si vous devez intercepter un large éventail, utilisez plutôt except Exception. Cela laisse quand même KeyboardInterrupt et SystemExit se propager.

2. Gardez les blocs Try petits

Ne mettez que le code susceptible de lever l'exception à l'intérieur du bloc try. Les grands blocs try rendent difficile l'identification de la ligne qui a causé l'erreur.

# BAD - too much code in try
try:
    data = load_data()
    cleaned = clean_data(data)
    result = analyze(cleaned)
    save_results(result)
except Exception as e:
    print(f"Something failed: {e}")
 
# GOOD - narrow try blocks
data = load_data()
cleaned = clean_data(data)
 
try:
    result = analyze(cleaned)
except ValueError as e:
    print(f"Analysis failed: {e}")
    result = default_result()
 
save_results(result)

3. Enregistrez les exceptions, ne les masquez pas

Avaler les exceptions avec pass crée des bugs invisibles. Enregistrez ou signalez toujours l'erreur.

import logging
 
try:
    process_record(record)
except ValueError as e:
    logging.error(f"Failed to process record {record['id']}: {e}")

4. Utilisez des types d'exceptions spécifiques

Interceptez le type d'exception le plus spécifique possible. Cela empêche de gérer accidentellement des erreurs que vous n'avez pas anticipées.

ApprocheIntercepteNiveau de risque
except:Tout y compris SystemExitTrès élevé
except Exception:Toutes les exceptions standardÉlevé
except ValueError:Uniquement ValueErrorFaible
except (ValueError, TypeError):Deux types spécifiquesFaible

5. Nettoyez les ressources dans Finally ou utilisez des gestionnaires de contexte

Pour les descripteurs de fichiers, les connexions de base de données et les verrous, utilisez toujours finally ou (encore mieux) une instruction with.

# Prefer context managers for resource cleanup
with open("data.txt", "r") as f:
    content = f.read()
# File is automatically closed, even if an exception occurs

Exemples concrets

Lire et analyser un fichier JSON

import json
 
def read_json_config(filepath):
    """Read a JSON configuration file with proper error handling."""
    try:
        with open(filepath, "r") as f:
            config = json.load(f)
    except FileNotFoundError:
        print(f"Config file not found: {filepath}")
        return None
    except PermissionError:
        print(f"No permission to read: {filepath}")
        return None
    except json.JSONDecodeError as e:
        print(f"Invalid JSON at line {e.lineno}, column {e.colno}: {e.msg}")
        return None
    else:
        print(f"Loaded config with keys: {list(config.keys())}")
        return config
 
config = read_json_config("app_config.json")
if config:
    db_host = config.get("database_host", "localhost")

Effectuer des appels API HTTP

import urllib.request
import urllib.error
import json
 
def fetch_user(user_id):
    """Fetch user data from an API with retry logic."""
    url = f"https://jsonplaceholder.typicode.com/users/{user_id}"
    max_retries = 3
 
    for attempt in range(1, max_retries + 1):
        try:
            with urllib.request.urlopen(url, timeout=5) as response:
                data = json.loads(response.read().decode())
            return data
        except urllib.error.HTTPError as e:
            if e.code == 404:
                print(f"User {user_id} not found.")
                return None
            print(f"HTTP error {e.code} on attempt {attempt}")
        except urllib.error.URLError as e:
            print(f"Network error on attempt {attempt}: {e.reason}")
        except json.JSONDecodeError:
            print("API returned invalid JSON.")
            return None
 
    print(f"All {max_retries} attempts failed.")
    return None
 
user = fetch_user(1)
if user:
    print(f"Found user: {user['name']}")

Traitement de données CSV

import csv
 
def process_sales_data(filepath):
    """Process a CSV file with robust error handling."""
    results = []
 
    try:
        with open(filepath, "r", newline="") as f:
            reader = csv.DictReader(f)
            for row_num, row in enumerate(reader, start=2):
                try:
                    amount = float(row["amount"])
                    quantity = int(row["quantity"])
                    results.append({
                        "product": row["product"],
                        "total": amount * quantity,
                    })
                except KeyError as e:
                    print(f"Row {row_num}: Missing column {e}")
                except ValueError as e:
                    print(f"Row {row_num}: Invalid number - {e}")
    except FileNotFoundError:
        print(f"File not found: {filepath}")
    except PermissionError:
        print(f"Cannot read file: {filepath}")
 
    return results

Try/Except vs If/Else : Quand utiliser chacun

Python suit le principe EAFP : "Easier to Ask Forgiveness than Permission" (Il est plus facile de demander pardon que la permission). Cela contraste avec l'approche LBYL : "Look Before You Leap" (Regarder avant de sauter).

# LBYL (Look Before You Leap) - using if/else
if "email" in user_data:
    email = user_data["email"]
else:
    email = "unknown"
 
# EAFP (Easier to Ask Forgiveness) - using try/except
try:
    email = user_data["email"]
except KeyError:
    email = "unknown"
Critèreif/else (LBYL)try/except (EAFP)
Meilleur quandLa vérification est peu coûteuse et l'erreur est fréquenteL'erreur est rare ou la vérification est coûteuse
Performance (pas d'erreur)Légèrement plus lent (vérification supplémentaire à chaque fois)Légèrement plus rapide (pas de surcoût de vérification)
Performance (erreur survient)IdentiquePlus lent (la création d'exception a un surcoût)
Conditions de coursePossibles (l'état peut changer entre la vérification et l'utilisation)Aucune (opération atomique)
LisibilitéClaire pour les conditions simplesMeilleure pour les opérations pouvant échouer de multiples façons
Opérations fichierif os.path.exists(path) -- le fichier pourrait être supprimé entre la vérification et l'ouverturetry: open(path) -- gère l'échec réel
Accès dictionnaireif key in dict -- simple et rapidetry: dict[key] -- ou utilisez simplement dict.get(key, default)

Utilisez try/except quand :

  • Le cas d'échec est rare (les exceptions sont optimisées pour le chemin "sans erreur").
  • La vérification elle-même est aussi coûteuse que l'opération (par exemple, vérifier si un fichier existe puis l'ouvrir).
  • Plusieurs choses peuvent mal tourner dans une séquence d'opérations.
  • Les conditions de course sont une préoccupation (surtout avec les fichiers et les ressources réseau).

Utilisez if/else quand :

  • La condition est peu coûteuse à vérifier et les échecs sont fréquents.
  • Vous validez l'entrée utilisateur avant le traitement.
  • La logique se lit plus clairement comme un conditionnel.

Déboguer les exceptions plus rapidement avec RunCell

Quand vous travaillez dans des notebooks Jupyter, les exceptions peuvent interrompre votre flux d'analyse. Vous rencontrez un KeyError à la ligne 50 000 d'un DataFrame, ou un TypeError apparaît trois cellules en profondeur dans un pipeline. Trouver la cause racine signifie faire défiler les tracebacks et inspecter les variables manuellement.

RunCell (opens in a new tab) est un agent IA qui s'exécute directement dans Jupyter. Il lit le traceback complet, inspecte les variables dans votre portée actuelle et suggère une correction en contexte. Voici comment il aide avec la gestion des exceptions :

  • Analyse du traceback. RunCell analyse la chaîne d'exceptions et identifie quelle variable ou opération a causé l'échec, même dans les appels de fonctions imbriqués.
  • Suggestions de corrections. Au lieu de chercher sur Stack Overflow, RunCell génère une cellule de code corrigée que vous pouvez exécuter immédiatement. Il sait s'il faut ajouter un try/except, corriger une conversion de type ou gérer une clé manquante.
  • Vérifications préventives. RunCell peut scanner votre code et signaler les opérations susceptibles de lever des exceptions -- comme accéder à des clés de dictionnaire sans .get(), ou diviser sans vérifier le zéro -- avant que vous n'exécutiez la cellule.

Comme RunCell fonctionne dans votre environnement Jupyter existant, il a accès à vos données et variables réelles. Les suggestions qu'il fournit sont spécifiques à votre situation, pas des conseils génériques.

FAQ

Quelle est la différence entre try/except et try/catch en Python ?

Python utilise try/except, pas try/catch. La syntaxe try/catch appartient à des langages comme Java, JavaScript et C++. En Python, le mot-clé est except. La fonctionnalité est la même : vous tentez du code qui pourrait échouer et définissez un gestionnaire pour le cas d'échec.

Puis-je utiliser plusieurs blocs except en Python ?

Oui. Vous pouvez enchaîner autant de blocs except que nécessaire, chacun interceptant un type d'exception différent. Python les évalue dans l'ordre et exécute le premier bloc correspondant. Vous pouvez également intercepter plusieurs exceptions dans un seul bloc en utilisant un tuple : except (ValueError, TypeError) as e:.

Quand dois-je utiliser else avec try/except ?

Utilisez le bloc else quand vous avez du code qui ne doit s'exécuter que si le bloc try a réussi. Le principal avantage est que les exceptions levées dans le bloc else ne sont pas interceptées par les blocs except précédents, ce qui empêche de masquer accidentellement des erreurs sans rapport.

Est-ce que finally s'exécute toujours en Python ?

Oui. Le bloc finally s'exécute que le bloc try se soit terminé normalement, ait levé une exception gérée ou ait levé une exception non gérée. Il s'exécute même si le bloc try ou except contient une instruction return. Les seules exceptions sont si le processus Python est tué de l'extérieur ou si os._exit() est appelé.

Comment créer des exceptions personnalisées en Python ?

Créez une nouvelle classe qui hérite de Exception. Vous pouvez ajouter des attributs personnalisés et redéfinir la méthode __init__. Par exemple : class MyError(Exception): pass. Pour des cas plus complexes, ajoutez des champs comme des codes d'erreur ou des données de contexte. Héritez toujours de Exception, pas de BaseException.

Conclusion

Le try/except de Python est le mécanisme standard pour gérer les erreurs d'exécution. Il vous permet d'intercepter des exceptions spécifiques, d'exécuter du code de nettoyage et de maintenir la stabilité de vos programmes quand les choses tournent mal. Le patron complet -- try/except/else/finally -- couvre chaque scénario : tenter l'opération, gérer les échecs, exécuter la logique de succès et nettoyer les ressources.

Les principes clés sont simples. Interceptez des exceptions spécifiques, pas génériques. Gardez vos blocs try petits. Enregistrez ou signalez toujours les erreurs au lieu de les masquer. Utilisez finally ou les gestionnaires de contexte pour le nettoyage. Levez des exceptions significatives dans votre propre code avec des messages d'erreur clairs.

Que vous lisiez des fichiers, fassiez des appels API ou traitiez des entrées utilisateur, une gestion correcte des exceptions fait la différence entre un script qui plante à 2 heures du matin et un qui enregistre l'erreur et continue de fonctionner. Commencez par les bases -- un simple try/except autour du code qui pourrait échouer -- et évoluez vers des hiérarchies d'exceptions personnalisées à mesure que vos projets grandissent.

📚