Kontext-Manager ist ein Objekt, das das Verhalten beim Eintritt und Austritt aus einem with-Block definiert und eine automatische Verwaltung von Ressourcen (Dateien, Verbindungen usw.) gewährleistet. Er wird durch die Methoden __enter__ und __exit__ in einer Klasse oder über den Dekorator @contextmanager aus dem Modul contextlib implementiert.
class FileManager: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None def __enter__(self): self.file = open(self.filename, self.mode) return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() with FileManager('test.txt', 'w') as f: f.write('Hello')
Feinheiten: Es ist wichtig, Ausnahmen in __exit__ richtig zu behandeln, indem man True zurückgibt, um Fehler zu unterdrücken, und vorsichtig mit Ressourcen umzugehen, um zu verhindern, dass sie bei Fehlern offen bleiben.
Frage: Wenn eine Ausnahme im with-Block auftritt, ruft Python dann die Methode __exit__ auf? Wie werden die Fehlerparameter übergeben?
Antwort: Ja, die Methode __exit__ wird immer aufgerufen, auch wenn eine Ausnahme auftritt. Dabei werden der Typ der Ausnahme, ihr Wert und der Traceback übergeben. Wenn __exit__ True zurückgibt, wird die Ausnahme unterdrückt.
class Simple: def __enter__(self): print('Enter') def __exit__(self, exc_type, exc_val, exc_tb): print('Exit') print(exc_type, exc_val) return True # Fehler "fliegt" nicht nach außen with Simple(): raise ValueError('boom!')
Geschichte
__exit__ in einem selbstgeschriebenen Manager für Dateideskriptoren zu implementieren — im Falle einer Ausnahme wurde die Datei nicht geschlossen, was zu einer Leckage von Dateideskriptoren und Ausfällen bei der Arbeit mit einer großen Anzahl von Dateien führte.Geschichte
True in __exit__ zurückgab. Dies hat Fehler "gedämpft" und führte zu unbemerksamen Störungen und Datenintegritätsverletzungen, da die Logik annahm, dass die Transaktion erfolgreich war.Geschichte
Nutzten den Dekorator @contextmanager aus dem Modul contextlib, vergaßen jedoch, Ausnahmen innerhalb von Yield zu behandeln, was dazu führte, dass die Verbindung mit dem Socket bei einem Absturz des Codes nicht geschlossen wurde und der Server mit offenen Ports "hing".