Menu

Python-Context-Manager: die with-Anweisung erklärt

Was die with-Anweisung wirklich tut — automatisches Aufräumen für Dateien, Locks, Datenbankverbindungen und alles andere, das zuverlässig geschlossen werden muss.

Die Anweisung, die hinter sich aufräumt

Jede Ressource, die du in einem Programm öffnest — eine Datei, eine Netzwerkverbindung, ein Datenbank-Handle, ein Lock — muss geschlossen werden, wenn du fertig bist. Vergiss es, und du leakst Speicher, blockierst andere Prozesse oder beschädigst Dateien bei Abstürzen. Pythons with-Anweisung erledigt das für dich.

Das Muster, mit dem alle anfangen, ist eine Datei lesen:

with open("notes.txt") as f:
    contents = f.read()
    print(contents)

Zwei Dinge passieren automatisch. Beim Eintritt gibt open() dir ein Datei-Objekt, gebunden an f. Beim Austritt — egal, ob der Block normal endet, früh zurückkehrt oder eine Ausnahme wirft — ruft Python f.close() für dich auf.

Das ist es. Das ist der ganze Punkt von with.

Was „With“ ersetzt

Vor Context Managern war das Äquivalent ein try/finally:

f = open("notes.txt")
try:
    contents = f.read()
    print(contents)
finally:
    f.close()

Fünf Zeilen Zeremonie für „Datei lesen und danach schließen“. Multiplizier das mit jedem open in einem größeren Programm, und der Reiz wird deutlich. with ist kürzer, schwerer falsch zu machen und unmöglich zu vergessen.

Mehrere Ressourcen öffnen

Du kannst mehrere Context Manager in einem with binden:

with open("input.txt") as src, open("output.txt", "w") as dst:
    dst.write(src.read().upper())

Beide Dateien öffnen beim Eintritt, beide schließen beim Austritt. Wenn das erste open gelingt und das zweite fliegt, schließt Python trotzdem das erste — die Maschinerie handhabt Teilaufbauten korrekt.

Für längere Ressourcenlisten ist die Klammern-Form (Python 3.10+) klarer:

with (
    open("a.txt") as a,
    open("b.txt") as b,
    open("c.txt") as c,
):
    ...

Was ein Context Manager tatsächlich ist

Jedes Objekt, das __enter__ und __exit__ definiert, ist ein Context Manager. Das Protokoll ist kinderleicht:

  • __enter__(self) läuft, wenn der with-Block startet. Der Rückgabewert ist das, woran as name bindet.
  • __exit__(self, exc_type, exc_value, traceback) läuft, wenn der Block endet — egal wie. Hat eine Ausnahme den Austritt verursacht, werden ihre Infos mitgegeben, damit der Context Manager sie prüfen oder unterdrücken kann.

Hier ein minimaler, der den umhüllten Block zeitmisst:

main.py
Output
Click Run to see the output here.

with Timer(): erzeugt das Objekt, ruft __enter__, führt den Rumpf aus, ruft __exit__. Keine Datei, kein Lock — nur eine kleine Hülle um „etwas tun, messen, wie lange es gedauert hat“.

Die contextlib.contextmanager-Abkürzung

Für jeden Context Manager eine Klasse zu definieren ist schwerer als nötig. contextlib.contextmanager macht aus einer Generator-Funktion einen Context Manager — ein yield trennt das „Vorher“ vom „Nachher“:

main.py
Output
Click Run to see the output here.

Alles vor yield ist das __enter__-Verhalten. Alles danach ist das __exit__. Das try/finally sorgt dafür, dass das Aufräumen läuft, auch wenn der Rumpf fliegt.

Die meisten eigenen Context Manager, die du schreibst, passen in diese Form. Greif zuerst zur Dekorator-Form; wechsel zu einer Klasse nur, wenn du etwas brauchst, das die Generator-Form nicht ausdrücken kann.

Etwas vorübergehend ändern

Ein häufiges Muster: etwas setzen, nutzen, zurücksetzen. Context Manager drücken das sauber aus:

main.py
Output
Click Run to see the output here.

Jedes „setzen, dann zurücksetzen“-Muster — Umgebungsvariablen, Log-Level, Feature Flags, Test-Fixtures — passt natürlich in einen Context Manager. Aufruferinnen müssen nicht daran denken, etwas zurückzusetzen.

Ausnahmen unterdrücken

Die __exit__-Methode darf True zurückgeben, um Python zu sagen „ich habe die Ausnahme behandelt; schluck sie“. Das ist selten und meist ein Warnsignal, aber so funktioniert contextlib.suppress:

main.py
Output
Click Run to see the output here.

suppress(FileNotFoundError) macht aus FileNotFoundError ein No-op. Nutz es für wirklich optionale Operationen — „versuch das, egal, ob es klappt“. Nutz es nicht, um Ausnahmen zu unterdrücken, über die du nicht nachgedacht hast.

Weitere Context Manager, denen du begegnest

Context Manager tauchen überall in der Standardbibliothek auf, sobald du hinschaust:

import threading
from pathlib import Path

# Locks — guarantee release even if the critical section raises.
lock = threading.Lock()
with lock:
    ...

# tempfile — delete the temp file when you're done.
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
    path = Path(tmp) / "scratch.txt"
    path.write_text("hello")

# Database connections — close the connection (or end the transaction).
import sqlite3
with sqlite3.connect(":memory:") as conn:
    conn.execute("CREATE TABLE t (x INTEGER)")

Drittanbieter-Bibliotheken folgen denselben Konventionen. Wenn du with something as x: siehst, bedeutet es fast immer „nutz x für die Dauer dieses Blocks und räum danach auf“.

Wann du kein with nutzt

  • Wenn du gar kein Setup und Teardown hast. Beliebigen Code ohne Grund in einen Context Manager zu wickeln, macht Lärm.
  • Wenn du die Ressource über viele unabhängige Blöcke brauchst. Ein with über die gesamte Laufzeit eines langen Skripts zu halten kann verschleiern, was der Aufräumscope tatsächlich ist. Denk lieber an eine Klasse, der die Ressource gehört.
  • Wenn ein Dekorator besser passt. Manche Wiederholungsmuster (Retry, Log, Timing) lesen sich als @decorator an einer Funktion natürlicher als als with ...: drin. Wähl, was sich am Aufrufort besser liest.

Meistens ist with richtig. Die seltenen Ausnahmen fallen einem leicht auf, sobald man nach ihnen sucht.

Als Nächstes: Arbeiten mit echten Dateien

Du kennst jetzt die Mechanik hinter with open(...) as f: — den Kontext, in dem du es in 90 Prozent der Fälle nutzt. Das nächste Kapitel setzt sie ein, um Dateien auf der Platte zu lesen, zu schreiben und zu navigieren.

Häufig gestellte Fragen

Was macht with open in Python?

with open(path) as f: öffnet die Datei und bindet sie für die Dauer des Blocks an f. Endet der Block — normal oder wegen einer Ausnahme —, schließt Python die Datei automatisch. Du brauchst kein f.close(); die with-Anweisung garantiert es.

Warum with statt reinem open()?

Weil with die Datei schließt, auch wenn mitten im Block eine Ausnahme fliegt. Bei reinem open() liegt die Pflicht close() in jedem Pfad bei dir, einschließlich der Fehlerpfade. with ist sicherer und kürzer.

Wie öffne ich mehrere Dateien in einer with-Anweisung?

Trenn die Context Manager mit Kommas: with open('a.txt') as a, open('b.txt') as b:. Beide Dateien werden beim Eintritt geöffnet und beim Austritt in umgekehrter Reihenfolge geschlossen. Das ersetzt verschachtelte with-Anweisungen, wenn du mehrere Ressourcen gleichzeitig brauchst.

Lerne mit Coddy zu programmieren

LOS GEHT'S