L'instruction with en Python

Dans cet article, nous allons aborder l'instruction with en Python, qui est essentielle pour gérer les ressources telles que les fichiers et les connexions réseau.

Nous verrons comment cette instruction fonctionne, les scénarios d'utilisation courants et des exemples détaillés pour vous aider à comprendre et à maîtriser cette fonctionnalité de Python.

Pourquoi utiliser l'instruction with

L'instruction with simplifie la gestion des ressources en garantissant que les opérations d'initialisation et de finalisation sont effectuées de manière cohérente et fiable.

Par exemple, lors de l'ouverture d'un fichier, il est important de le fermer correctement après avoir terminé de l'utiliser.

Avec l'instruction with, vous n'avez pas besoin de vous soucier de fermer le fichier manuellement :

with open('mon_fichier.txt', 'r') as fichier:
    contenu = fichier.read()

Dans cet exemple, le fichier sera automatiquement fermé à la fin du bloc with, même en cas d'exception.

Le protocole de gestion des contextes

L'instruction with repose sur le protocole de gestion des contextes, qui est défini par deux méthodes spéciales : __enter__ et __exit__.

Un objet implémentant ces deux méthodes est appelé un objet de contexte.

Lorsqu'un objet de contexte est utilisé dans une déclaration with, la méthode __enter__ est appelée au début du bloc, et la méthode __exit__ est appelée à la fin.

Prenons l'exemple d'un fichier :

with open('mon_fichier.txt', 'r') as fichier:
    contenu = fichier.read()

Lorsque le bloc with commence, la méthode open est appelée, et retourne un objet fichier.

Cet objet implémente les méthodes __enter__ et __exit__.

La méthode __enter__ est alors appelée, et le fichier est ouvert.

À la fin du bloc with, la méthode __exit__ est appelée, et le fichier est fermé automatiquement.

Créer vos propres objets de contexteformat_paragraph La bonne nouvelle, c'est que vous pouvez créer vos propres objets de contexte en définissant les méthodes __enter__ et __exit__ dans votre classe.

Par exemple, voici comment créer un objet de contexte pour mesurer le temps d'exécution d'un bloc de code :

import time

class Chronometre:
    def __enter__(self):
        self.debut = time.time()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.fin = time.time()
        self.duree = self.fin - self.debut
        print(f"Temps écoulé : {self.duree:.2f} secondes")

with Chronometre() as chrono:
    # Bloc de code dont vous souhaitez mesurer le temps d'exécution
    liste = [i**2 for i in range(100000)]

Dans cet exemple, la méthode __enter__ enregistre l'heure de début, et la méthode __exit__ enregistre l'heure de fin et affiche la durée.

Utiliser plusieurs objets de contexte

Il est possible d'utiliser plusieurs objets de contexte dans une seule déclaration with. Par exemple, si vous souhaitez copier le contenu d'un fichier dans un autre, vous pouvez le faire comme suit :

with open('fichier_source.txt', 'r') as fichier_source, open('fichier_destination.txt', 'w') as fichier_destination:
    for ligne in fichier_source:
        fichier_destination.write(ligne)

Dans cet exemple, deux objets de contexte sont utilisés simultanément pour ouvrir le fichier source en mode lecture et le fichier destination en mode écriture. Le contenu est ensuite copié ligne par ligne.

Les décorateurs de contexte

Python permet également de créer des décorateurs de contexte en utilisant des fonctions génératrices (yield)

Les décorateurs de contexte sont une alternative aux objets de contexte pour certaines situations.

Par exemple, voici un décorateur de contexte pour mesurer le temps d'exécution d'une fonction :

from contextlib import contextmanager
import time

@contextmanager
def chronometre():
    debut = time.perf_counter()
    yield
    fin = time.perf_counter()
    duree = fin - debut
    print(f"Temps écoulé : {duree:.2f} secondes")

# Utilisation du décorateur de contexte chronometre
with chronometre():
    # Bloc de code dont vous souhaitez mesurer le temps d'exécution
    liste = [i**2 for i in range(100000)]

Dans cet exemple, la fonction chronometre est définie comme un décorateur de contexte à l'aide du décorateur @contextmanager et de la fonction génératrice yield.