Passer au contenu principal
eLearner.app
Module 9 · Leçon 3 sur 435/36 dans le cours~12 min
Leçons du module (3/4)

Context managers : with et __enter__/__exit__

Un gestionnaire de contexte (context manager) est un objet que vous utilisez avec with. Il garantit que certaines opérations de configuration/nettoyage ont toujours lieu, même en cas d'erreurs. L'exemple canonique : ouvrir et fermer un fichier.

Le patron with

Python
with open("note.txt", "w") as f:
    f.write("ciao")
# qui f è già chiuso, anche se write() ha sollevato un'eccezione

En langage clair : "avec la ressource X ouverte sous le nom f, fais ceci. Une fois terminé (quoi qu'il arrive), ferme X." Sans with, vous devriez écrire un try/finally à chaque fois.

Plus d'un à la fois

Python
with open("a.txt") as f, open("b.txt") as g:
    dati_a = f.read()
    dati_b = g.read()

Écrire votre propre gestionnaire de contexte (classe)

Vous devez définir deux méthodes dunder : __enter__ et __exit__.

Python
class Timer:
    def __enter__(self):
        import time
        self.t0 = time.perf_counter()
        return self  # è l'oggetto che finisce dopo "as"
    def __exit__(self, exc_type, exc_value, traceback):
        import time
        self.elapsed = time.perf_counter() - self.t0
        # ritornare True ingoia l'eccezione; di solito non lo vuoi
        return False

with Timer() as t:
    sum(range(10**6))
t.elapsed  # quanto è durato
  • __enter__(self) est appelée à l'entrée du bloc ; la valeur renvoyée est ce que vous obtenez après as.
  • __exit__(self, exc_type, exc_value, tb) est appelée à la sortie (toujours). Si elle renvoie True, les exceptions sont supprimées.

Le raccourci : @contextmanager

contextlib.contextmanager vous permet d'écrire un gestionnaire de contexte sous forme de fonction générateur :

Python
from contextlib import contextmanager

@contextmanager
def timer():
    import time
    t0 = time.perf_counter()
    try:
        yield  # qui sta il blocco "with"
    finally:
        elapsed = time.perf_counter() - t0
        print(f"durata: {elapsed:.4f}s")

with timer():
    sum(range(10**6))

Le code avant yield correspond à __enter__, le code après à __exit__. Le bloc try/finally garantit le nettoyage même en cas d'erreurs.

suppress : ignorer une exception

Python
from contextlib import suppress

with suppress(FileNotFoundError):
    open("forse-non-esiste.txt").read()
# se il file non c'è, nessun errore propaga

Piège : with uniquement pour les objets qui le prennent en charge

Python
x = 42
with x:  # AttributeError: __enter__
    ...

Écrire des gestionnaires de contexte avec @contextmanager

Le module contextlib de la bibliothèque standard exporte un décorateur très pratique appelé @contextmanager pour créer des gestionnaires de contexte à l'aide de fonctions générateurs, au lieu d'écrire des classes verbeuses implémentant les méthodes __enter__ et __exit__. Il vous suffit d'écrire une fonction générateur utilisant un simple yield :

Python
from contextlib import contextmanager

@contextmanager
def manage_resource():
    # __enter__ code
    yield resource
    # __exit__ code

À vous de jouer

Exercice#python.m9.l3.e1
Tentatives : 0Chargement…

Utilisez contextlib.suppress pour essayer int('non-numero') en ignorant ValueError. Après le bloc, affectez `ok = True`. Évaluez `ok`.

Chargement de l'éditeur…
Afficher l'indice

suppress(ValueError) n'intercepte que ValueError.

Solution disponible après 3 tentatives

Exercice de révision

Exercice#python.m9.l3.e2
Tentatives : 0Chargement…

Écrivez un gestionnaire de contexte `collect` avec @contextmanager qui renvoie une liste vide et, à la sortie, la convertit en tuple (affichez-le). Utilisez-le pour ajouter 1, 2, 3 à la liste à l'intérieur du bloc. À la fin, évaluez `len(collection)` où `collection` est la liste renvoyée.

Chargement de l'éditeur…
Afficher l'indice

Utilisez yield items pour exposer la liste à l'intérieur du bloc with.

Solution disponible après 3 tentatives

Défi supplémentaire

Exercice#python.m9.l3.e3
Tentatives : 0Chargement…

Écrivez un gestionnaire de contexte personnalisé en utilisant le décorateur `@contextmanager` de `contextlib`. Le gestionnaire de contexte doit être nommé `mock_session`. À l'entrée, il doit ajouter `'start'` à une liste globale `log_list = []`, céder le contrôle avec `yield`, et à la sortie ajouter `'stop'`. Enfin, évaluez `log_list` après avoir utilisé le gestionnaire de contexte avec `with mock_session():`.

Chargement de l'éditeur…
Afficher l'indice

Utilisez yield à l'intérieur de mock_session pour suspendre l'exécution avant d'ajouter 'stop'.

Solution disponible après 3 tentatives