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 derwith-Block startet. Der Rückgabewert ist das, woranas namebindet.__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:
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“:
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:
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:
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
@decoratoran einer Funktion natürlicher als alswith ...: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 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()?
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?
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.