2.24. Menedżery kontekstu¶
Instrukcja with uruchamia kod konfiguracyjny, następnie ciało, a potem kod sprzątający – z gwarancją, że sprzątanie zostanie wykonane nawet wtedy, gdy ciało zawiedzie w połowie. Para metod dostarczająca konfigurację i sprzątanie nazywana jest menedżerem kontekstu.
Postać jest następująca:
with <expression> as <name>:
<body>
Wyrażenie zwraca menedżera kontekstu. Python wywołuje jego metodę __enter__, opcjonalnie wiąże wynik z <name>, uruchamia ciało, a następnie wywołuje __exit__ – niezależnie od tego, czy ciało zakończyło się normalnie, czy zgłosiło wyjątek.
__exit__ uruchamia się na końcu bloku niezależnie od tego, co się w nim wydarzy.¶
2.24.1. Używanie menedżera kontekstu¶
Kanonicznym przykładem jest otwarcie pliku:
with open("data.txt") as f:
text = f.read()
# f is now closed, even if read() failed
open() zwraca obiekt pliku, który sam jest menedżerem kontekstu; __enter__ zwraca plik, a __exit__ go zamyka. Blok with sprawia, że „zawsze zamykaj plik po zakończeniu” staje się domyślnym zachowaniem, a nie czymś, o czym wywołujący musi pamiętać.
2.24.1.1. Wiele menedżerów kontekstu¶
Pojedyncza instrukcja with może wejść w kilka menedżerów kontekstu naraz, rozdzielonych przecinkami:
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())
Równoważne dwóm zagnieżdżonym blokom with, ale bardziej płaskie. Menedżery są wprowadzane od lewej do prawej i opuszczane w odwrotnej kolejności; jeśli __enter__ na menedżerze po prawej zgłosi wyjątek, __exit__ menedżera po lewej nadal zostanie wykonany.
2.24.2. Pisanie menedżera kontekstu¶
Każda klasa z metodami __enter__ i __exit__ działa jako menedżer kontekstu:
class Section:
def __init__(self, label):
self.label = label
def __enter__(self):
print("---", self.label, "---")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("--- end", self.label, "---")
return False
with Section("setup"):
print("doing the work")
Wynik:
--- setup ---
doing the work
--- end setup ---
__exit__ otrzymuje trzy argumenty opisujące wyjątek, który zakończył blok, lub trzy wartości None, jeśli blok zakończył się normalnie. Zwrócenie False (lub None) pozwala dowolnemu wyjątkowi propagować się po sprzątaniu; zwrócenie True połknęłoby go.
Używaj menedżerów kontekstu dla każdego zasobu, który ma cykl życia „otwórz / zamknij” lub „pozyskaj / zwolnij” – nie tylko dla plików. Wzorzec ten utrzymuje sprzątanie sparowane z konfiguracją w miejscu, gdzie oba są wprowadzane, dzięki czemu zapomniane zamknięcie w środku długiej funkcji nie może doprowadzić do wycieku zasobu.