2.24. Context manager#
L’istruzione with esegue del codice di inizializzazione, poi un corpo, poi del codice di pulizia – con la garanzia che la pulizia venga eseguita anche quando il corpo fallisce a metà. La coppia di metodi che fornisce l’inizializzazione e la pulizia è chiamata context manager.
La forma è:
with <expression> as <name>:
<body>
L’espressione restituisce un context manager. Python ne chiama il metodo __enter__, opzionalmente lega il risultato a <name>, esegue il corpo e poi chiama __exit__ – sia che il corpo si sia completato normalmente sia che abbia sollevato un’eccezione.
__exit__ viene eseguito alla fine del blocco qualunque cosa accada al suo interno.#
2.24.1. Usare un context manager#
L’esempio canonico è l’apertura di un file:
with open("data.txt") as f:
text = f.read()
# f is now closed, even if read() failed
open() restituisce un oggetto file che è esso stesso un context manager; __enter__ restituisce il file, e __exit__ lo chiude. Il blocco with rende «chiudi sempre il file quando hai finito» il comportamento predefinito anziché qualcosa che il chiamante deve ricordarsi.
2.24.1.1. Più context manager#
Una singola istruzione with può entrare in più context manager contemporaneamente, separati da virgole:
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())
Equivale a due blocchi with annidati, ma più piatto. I manager vengono entrati da sinistra a destra e usciti in ordine inverso; se __enter__ sul manager di destra solleva un’eccezione, l”__exit__ del manager di sinistra viene comunque eseguito.
2.24.2. Scrivere un context manager#
Qualsiasi classe con i metodi __enter__ e __exit__ funziona come context manager:
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")
Output:
--- setup ---
doing the work
--- end setup ---
__exit__ riceve tre argomenti che descrivono l’eccezione che ha terminato il blocco, oppure tre valori None se il blocco si è completato normalmente. Restituire False (o None) lascia propagare qualsiasi eccezione dopo la pulizia; restituire True la sopprimerebbe.
Usa i context manager per qualsiasi risorsa che abbia un ciclo di vita «apri / chiudi» o «acquisisci / rilascia» – non solo i file. Il pattern mantiene la pulizia abbinata all’inizializzazione nel punto in cui entrambe vengono introdotte, così che una chiusura dimenticata nel mezzo di una funzione lunga non possa far trapelare la risorsa.