2.24. Správci kontextu

Příkaz with spustí inicializační kód, pak tělo a nakonec úklidový kód – se zárukou, že úklid proběhne i tehdy, když tělo někde uprostřed selže. Dvojici metod, která dodává inicializaci a úklid, se říká správce kontextu.

Tvar je:

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

Výraz vrací správce kontextu. Python zavolá jeho metodu __enter__, volitelně naváže výsledek na <name>, spustí tělo a poté zavolá __exit__ – ať už tělo proběhlo normálně, nebo vyvolalo výjimku.

Diagram toku: nejprve běží __enter__, pak tělo; tělo buď proběhne normálně, nebo vyvolá výjimku; tak či onak se na konci spustí __exit__.

__exit__ se spustí na konci bloku, ať se uvnitř něj stane cokoli.

2.24.1. Použití správce kontextu

Kanonickým příkladem je otevření souboru:

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

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

open() vrací souborový objekt, který je sám správcem kontextu; __enter__ vrátí soubor a __exit__ jej zavře. Blok with činí z „po dokončení vždy zavřít soubor“ výchozí chování místo něčeho, co si volající musí pamatovat.

2.24.1.1. Více správců kontextu

Jediný příkaz with může najednou vstoupit do několika správců kontextu oddělených čárkami:

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

Ekvivalentní dvěma vnořeným blokům with, ale plošší. Správci se zadávají zleva doprava a opouštějí se v opačném pořadí; pokud __enter__ u pravého správce vyvolá výjimku, __exit__ levého správce se přesto spustí.

2.24.2. Napsání správce kontextu

Jako správce kontextu funguje jakákoli třída s metodami __enter__ a __exit__:

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

Výstup:

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

__exit__ přijímá tři argumenty popisující výjimku, která ukončila blok, nebo tři hodnoty None, pokud blok proběhl normálně. Vrácení False (nebo None) nechá jakoukoli výjimku po úklidu propagovat dál; vrácení True by ji pohltilo.

Správce kontextu používejte pro jakýkoli prostředek, který má životní cyklus „otevřít / zavřít“ nebo „získat / uvolnit“ – nejen pro soubory. Tento vzor udržuje úklid spárovaný s inicializací v místě, kde se obojí zavádí, takže zapomenuté zavření uprostřed dlouhé funkce nemůže prostředek nechat uniknout.