2.24. Upravitelji konteksta

Naredba with pokreće kod za postavljanje, zatim tijelo, pa kod za pospremanje – uz jamstvo da se pospremanje izvrši čak i kad tijelo otkaže usred izvođenja. Par metoda koji pruža postavljanje i pospremanje naziva se upravitelj konteksta.

Oblik je:

with <expression> as <name>:
    <body>

Izraz vraća upravitelja konteksta. Python poziva njegovu metodu __enter__, po izboru veže rezultat na <name>, izvršava tijelo i zatim poziva __exit__ – bilo da je tijelo završilo normalno ili izazvalo iznimku.

Dijagram toka: __enter__ se izvršava prvi, zatim tijelo; tijelo ili završava normalno ili izaziva iznimku; u oba slučaja __exit__ se izvršava na kraju.

__exit__ se izvršava na kraju bloka što god se unutar njega dogodilo.

2.24.1. Korištenje upravitelja konteksta

Kanonski primjer je otvaranje datoteke:

with open("data.txt") as f:
    text = f.read()

# f is now closed, even if read() failed

open() vraća objekt datoteke koji je i sam upravitelj konteksta; __enter__ vraća datoteku, a __exit__ je zatvara. Blok with čini „uvijek zatvori datoteku kad završiš” zadanim ponašanjem umjesto nečega čega se pozivatelj mora sjetiti.

2.24.1.1. Više upravitelja konteksta

Jedna naredba with može odjednom ući u nekoliko upravitelja konteksta, odvojenih zarezima:

with open("input.txt") as src, open("output.txt", "w") as dst:
    dst.write(src.read())

Ekvivalentno dvama ugniježđenim blokovima with, ali pljosnatije. U upravitelje se ulazi slijeva nadesno, a izlazi obrnutim redoslijedom; ako __enter__ na desnom upravitelju izazove iznimku, __exit__ lijevog upravitelja se ipak izvrši.

2.24.2. Pisanje upravitelja konteksta

Bilo koja klasa s metodama __enter__ i __exit__ radi kao upravitelj konteksta:

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")

Izlaz:

--- setup ---
doing the work
--- end setup ---

__exit__ prima tri argumenta koji opisuju iznimku koja je okončala blok, ili tri None vrijednosti ako je blok završio normalno. Vraćanje False (ili None) dopušta da se svaka iznimka propagira nakon pospremanja; vraćanje True bi je progutalo.

Koristi upravitelje konteksta za svaki resurs koji ima životni ciklus „otvori / zatvori” ili „zauzmi / oslobodi” – ne samo za datoteke. Obrazac drži čišćenje uparenim s postavljanjem na mjestu gdje su oba uvedena, pa zaboravljeno zatvaranje usred dugačke funkcije ne može procuriti resurs.