2.24. Kontexthanterare

with-satsen kör uppstartskod, sedan en kropp och därefter nedmonteringskod – med en garanti att nedmonteringen körs även när kroppen misslyckas halvvägs. Paret av metoder som tillhandahåller uppstart och nedmontering kallas en kontexthanterare.

Formen är:

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

Uttrycket returnerar en kontexthanterare. Python anropar dess __enter__-metod, binder eventuellt resultatet till <name>, kör kroppen och anropar sedan __exit__ – oavsett om kroppen slutfördes normalt eller utlöste ett undantag.

Flödesdiagram: __enter__ körs först, sedan kroppen; kroppen antingen slutförs normalt eller utlöser ett undantag; oavsett vad körs __exit__ till slut.

__exit__ körs i slutet av blocket oavsett vad som händer inuti det.

2.24.1. Att använda en kontexthanterare

Det kanoniska exemplet är att öppna en fil:

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

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

open() returnerar ett filobjekt som självt är en kontexthanterare; __enter__ returnerar filen, och __exit__ stänger den. with-blocket gör ”stäng alltid filen när du är klar” till standard snarare än något som anroparen måste komma ihåg.

2.24.1.1. Flera kontexthanterare

En enda with-sats kan gå in i flera kontexthanterare samtidigt, åtskilda med kommatecken:

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

Likvärdigt med två nästlade with-block, men plattare. Hanterarna träds in från vänster till höger och avslutas i omvänd ordning; om __enter__ på den högra hanteraren utlöser ett undantag körs ändå den vänstra hanterarens __exit__.

2.24.2. Att skriva en kontexthanterare

Vilken klass som helst med metoderna __enter__ och __exit__ fungerar som en kontexthanterare:

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

Utmatning:

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

__exit__ tar emot tre argument som beskriver undantaget som avslutade blocket, eller tre None-värden om blocket slutfördes normalt. Att returnera False (eller None) låter eventuella undantag fortplanta sig efter nedmonteringen; att returnera True skulle svälja det.

Använd kontexthanterare för alla resurser som har en livscykel av typen ”öppna / stäng” eller ”reservera / frigör” – inte bara filer. Mönstret håller uppstädning kopplad till uppstart på den punkt där båda introduceras, så ett bortglömt close mitt i en lång funktion kan inte läcka resursen.