2.24. Kontextuskezelők¶
A with utasítás lefuttat egy beállító kódot, majd egy törzset, majd egy lebontó kódot – azzal a garanciával, hogy a lebontás akkor is lefut, ha a törzs félúton hibára fut. A beállítást és a lebontást biztosító metóduspáros neve kontextuskezelő.
Az alakja:
with <expression> as <name>:
<body>
A kifejezés egy kontextuskezelőt ad vissza. A Python meghívja annak __enter__ metódusát, opcionálisan hozzáköti az eredményt a <name> névhez, lefuttatja a törzset, majd meghívja az __exit__ metódust – akár normálisan fejeződött be a törzs, akár kivételt dobott.
Az __exit__ a blokk végén lefut, bármi is történik benne.¶
2.24.1. Egy kontextuskezelő használata¶
A klasszikus példa egy fájl megnyitása:
with open("data.txt") as f:
text = f.read()
# f is now closed, even if read() failed
Az open() egy fájlobjektumot ad vissza, amely maga is kontextuskezelő; az __enter__ visszaadja a fájlt, az __exit__ pedig bezárja azt. A with blokk az „a fájlt mindig zárd be, ha végeztél” megközelítést teszi alapértelmezetté ahelyett, hogy a hívónak kellene rá emlékeznie.
2.24.1.1. Több kontextuskezelő¶
Egyetlen with utasítás egyszerre több kontextuskezelőbe is beléphet, vesszővel elválasztva:
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())
Egyenértékű két egymásba ágyazott with blokkal, de laposabb. A kezelők balról jobbra lépnek be, és fordított sorrendben lépnek ki; ha a jobb oldali kezelő __enter__ metódusa kivételt dob, a bal oldali kezelő __exit__ metódusa akkor is lefut.
2.24.2. Egy kontextuskezelő megírása¶
Bármely olyan osztály, amelynek van __enter__ és __exit__ metódusa, kontextuskezelőként működik:
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")
Kimenet:
--- setup ---
doing the work
--- end setup ---
Az __exit__ három argumentumot kap, amelyek a blokkot lezáró kivételt írják le, vagy három None értéket, ha a blokk normálisan fejeződött be. A False (vagy None) visszaadása esetén a lebontás után bármely kivétel tovább terjedhet; a True visszaadása elnyelné azt.
Használj kontextuskezelőket bármely olyan erőforráshoz, amelynek „megnyit / bezár” vagy „lefoglal / felszabadít” életciklusa van – nem csak fájlokhoz. A minta a takarítást a beállítás mellett tartja ott, ahol mindkettő bevezetésre kerül, így egy hosszú függvény közepén elfelejtett bezárás nem szivárogtathatja el az erőforrást.