Direkt zum Hauptinhalt springen
eLearner.app
Modul 9 · Lektion 3 von 435/36 im Kurs~12 min
Lektionen des Moduls (3/4)

Context Manager: with und __enter__/__exit__

Ein Kontextmanager (context manager) ist ein Objekt, das du mit with verwendest. Er garantiert, dass bestimmte Setup-/Cleanup-Operationen immer stattfinden, selbst wenn Fehler auftreten. Das kanonische Beispiel: Öffnen und Schließen einer Datei.

Das with-Muster

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

Einfach ausgedrückt: "mit Ressource X geöffnet als f, tue dies. Wenn fertig (was auch immer passiert), schließe X." Ohne with müsstest du jedes Mal ein try/finally schreiben.

Mehrere gleichzeitig

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

Einen eigenen Kontextmanager schreiben (Klasse)

Du musst zwei Dunder-Methoden definieren: __enter__ und __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) wird beim Betreten des Blocks aufgerufen; der zurückgegebene Wert ist das, was du nach as erhältst.
  • __exit__(self, exc_type, exc_value, tb) wird beim Verlassen aufgerufen (immer). Wenn sie True zurückgibt, werden alle Ausnahmen unterdrückt.

Die Abkürzung: @contextmanager

contextlib.contextmanager ermöglicht es dir, einen Kontextmanager als Generatorfunktion zu schreiben:

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))

Der Code vor yield entspricht __enter__, der Code danach __exit__. Das try/finally garantiert das Cleanup, selbst wenn Fehler auftreten.

suppress: Eine Ausnahme ignorieren

Python
from contextlib import suppress

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

Falle: with nur für Objekte, die es unterstützen

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

Kontextmanager mit @contextmanager schreiben

Das Modul contextlib der Standardbibliothek exportiert einen sehr praktischen Dekorator namens @contextmanager, um Kontextmanager mithilfe von Generatorfunktionen zu erstellen, anstatt wortreiche Klassen zu schreiben, die die Methoden __enter__ und __exit__ implementieren. Du schreibst einfach eine Generatorfunktion, die ein einziges yield verwendet:

Python
from contextlib import contextmanager

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

Probiere es aus

Übung#python.m9.l3.e1
Versuche: 0Wird geladen…

Verwende contextlib.suppress, um int('non-numero') auszuprobieren und dabei ValueError zu ignorieren. Weise nach dem Block `ok = True` zu. Evaluiere `ok`.

Editor wird geladen…
Hinweis anzeigen

suppress(ValueError) fängt nur ValueError ab.

Lösung nach 3 Versuchen verfügbar

Wiederholungsübung

Übung#python.m9.l3.e2
Versuche: 0Wird geladen…

Schreibe einen Kontextmanager `collect` mit @contextmanager, der eine leere Liste zurückgibt (yield) und diese beim Verlassen in ein Tupel umwandelt (gib es aus). Verwende ihn, um 1, 2, 3 an die Liste im Block anzuhängen. Evaluiere am Ende `len(collection)`, wobei `collection` die zurückgegebene Liste ist.

Editor wird geladen…
Hinweis anzeigen

Verwende yield items, um die Liste im with-Block bereitzustellen.

Lösung nach 3 Versuchen verfügbar

Zusätzliche Herausforderung

Übung#python.m9.l3.e3
Versuche: 0Wird geladen…

Schreibe einen benutzerdefinierten Kontextmanager mit dem Dekorator `@contextmanager` aus `contextlib`. Der Kontextmanager muss den Namen `mock_session` tragen. Beim Betreten muss er `'start'` an eine globale Liste `log_list = []` anhängen, die Steuerung mit `yield` abgeben und beim Verlassen `'stop'` anhängen. Evaluiere schließlich `log_list`, nachdem du den Kontextmanager mit `with mock_session():` verwendet hast.

Editor wird geladen…
Hinweis anzeigen

Verwende yield innerhalb von mock_session, um die Ausführung auszusetzen, bevor 'stop' angehängt wird.

Lösung nach 3 Versuchen verfügbar