24.2 Hilfsfunktionen für with-Kontexte – contextlib 

Das Modul contextlib der Standardbibliothek enthält Funktionen und Dekoratoren zum Umgang mit with-Kontexten. So können beispielsweise Objekte zu Kontext-Managern erweitert werden, die eigentlich nicht für diesen Zweck geschaffen wurden. Andere Funktionen in contextlib ermöglichen es, bestimmte Exceptions zu unterdrücken oder Bildschirmausgaben umzuleiten.
24.2.1 Einfache Funktionen als Kontext-Manager 

Ein klassischer Kontext-Manager, der in einer with-Anweisung verwendet werden kann, ist eine Klasse, die die Methoden __enter__ und __exit__ implementiert. Der Dekorator contextmanager aus dem Modul contextlib erlaubt es, wesentlich einfacher gestrickte Kontext-Manager auf der Basis einer Funktion zu schreiben.
Eine Funktion, die mit contextmanager dekoriert wird, muss ein Generatorobjekt[ 107 ](Näheres zu Generatoren erfahren Sie in Abschnitt 23.2. ) über genau ein Element zurückgeben:
import contextlib
import time
@contextlib.contextmanager
def laufzeit():
start = time.perf_counter()
try:
yield
finally:
print("Laufzeit: {:.2f} s".format(time.perf_counter() - start))
Im Beispiel wurde die parameterlose Funktion laufzeit mit contextmanager dekoriert. Technisch gesehen muss sie jetzt einen Generator über ein einziges Element zurückgeben. Dies wird durch die yield-Anweisung im Funktionskörper realisiert. Der Funktionskörper kann jetzt gedanklich in zwei Bereiche unterteilt werden: den Bereich vor yield und den Bereich danach.
Bei der Verwendung von laufzeit in einem with-Kontext wird der Körper der with-Anweisung genau dann ausgeführt, wenn die dekorierte Funktion laufzeit ihren Wert zurückgibt. Der Bereich der Funktion laufzeit vor der yield-Anweisung entspricht also der Methode __enter__ in einem klassischen Kontext-Manager. Analog entspricht der Bereich nach der yield-Anweisung der Methode __exit__.
Die zu einem Kontext-Manager ausgebaute Funktion laufzeit kann jetzt zur Laufzeitmessung von Programmabschnitten verwendet werden:
>>> with laufzeit():
... x = 0
... for i in range(10000000):
... x += (-1)**i * i
...
Laufzeit: 5.09 s
24.2.2 Bestimmte Exception-Typen unterdrücken 

Das Modul contextlib enthält den Kontext-Manager suppress, der Exceptions bestimmter Typen unterdrückt, wenn sie im with-Kontext geworfen werden:
>>> import contextlib
>>> x = ["Ich erscheine"]
>>> with contextlib.suppress(IndexError):
... print(x[0])
... print(x[1])
...
Ich erscheine
Dieser Code ist äquivalent zu der folgenden try/except-Anweisung, die etwas schlechter lesbar ist:
>>> x = ["Ich erscheine"]
>>> try:
... print(x[0])
... print(x[1])
... except IndexError:
... pass
...
Ich erscheine
Der Funktion suppress können beliebig viele Exception-Typen als Parameter übergeben werden.
24.2.3 Den Standard-Ausgabestrom umleiten 

Mit dem Kontext-Manager redirect_stdout aus dem Modul contextlib können Sie den Standard-Ausgabestrom, der zum Beispiel für Bildschirmausgaben mittels print verwendet wird, umleiten:
>>> import contextlib
>>> with open("out.txt", "w") as f_out:
... with contextlib.redirect_stdout(f_out):
... print("Bildschirm-")
... print("Ausgabe")
Im oben dargestellten Beispielprogramm wurde redirect_stdout dazu verwendet, alle Bildschirmausgaben in die Datei out.txt umzuleiten. Der Code ist äquivalent zu:
>>> import sys
>>> with open("out.txt", "w") as f_out:
... sys.stdout = f_out
... print("Bildschirm-")
... print("Ausgabe")
... sys.stdout = sys.__stdout__
Auch die Funktion redirect_stdout überschreibt die globale Referenz sys.stdout. Sie sollten daher auf mögliche Seiteneffekte achten.
Seit Python 3.5 existiert der Kontextmanager redirect_stderr, der analog zu redirect_stdout arbeitet, aber den Standardfehlerstrom stderr umleitet.