Python Language
Gestori di contesto ("con" istruzione)
Ricerca…
introduzione
Sebbene i gestori di contesto di Python siano ampiamente utilizzati, pochi capiscono lo scopo dietro il loro uso. Queste istruzioni, comunemente utilizzate con i file di lettura e scrittura, aiutano l'applicazione a conservare la memoria di sistema e a migliorare la gestione delle risorse assicurando che risorse specifiche siano utilizzate solo per determinati processi. Questo argomento spiega e dimostra l'uso dei gestori di contesto di Python.
Sintassi
- con "context_manager" (come "alias") (, "context_manager" (come "alias")?) *:
Osservazioni
I gestori di contesto sono definiti in PEP 343 . Sono destinati a essere usati come meccanismo più succinto per la gestione delle risorse rispetto a try ... finally
costrutti. La definizione formale è la seguente.
In questo PEP, i gestori di contesto forniscono i
__enter__()
e__exit__()
che vengono richiamati all'ingresso e all'uscita dal corpo__enter__()
with.
Quindi continua a definire l'istruzione with
come segue.
with EXPR as VAR: BLOCK
La traduzione della dichiarazione di cui sopra è:
mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) exc = True try: try: VAR = value # Only if "as VAR" is present BLOCK except: # The exceptional case is handled here exc = False if not exit(mgr, *sys.exc_info()): raise # The exception is swallowed if exit() returns true finally: # The normal and non-local-goto cases are handled here if exc: exit(mgr, None, None, None)
Introduzione ai gestori di contesto e alla dichiarazione con
Un gestore di contesto è un oggetto che viene avvisato quando un contesto (un blocco di codice) inizia e finisce . Di solito ne usi uno con l'istruzione with
. Si prende cura della notifica.
Ad esempio, gli oggetti file sono gestori contesto. Al termine di un contesto, l'oggetto file viene chiuso automaticamente:
open_file = open(filename)
with open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
L'esempio sopra è di solito semplificato usando la parola chiave as
:
with open(filename) as open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
Tutto ciò che termina l'esecuzione del blocco causa il richiamo del metodo di uscita del gestore contesto. Ciò include le eccezioni e può essere utile quando un errore causa l'uscita prematura da un file o una connessione aperti. Uscire da uno script senza chiudere correttamente i file / le connessioni è una cattiva idea, che potrebbe causare la perdita di dati o altri problemi. Utilizzando un gestore di contesto è possibile garantire che vengano sempre adottate precauzioni per prevenire danni o perdite in questo modo. Questa funzione è stata aggiunta in Python 2.5.
Assegnare a un bersaglio
Molti gestori di contesto restituiscono un oggetto una volta inseriti. È possibile assegnare quell'oggetto a un nuovo nome nell'istruzione with
.
Ad esempio, l'utilizzo di una connessione al database in un'istruzione with
potrebbe fornire un oggetto cursore:
with database_connection as cursor:
cursor.execute(sql_query)
Gli oggetti File si restituiscono da soli, ciò rende possibile sia aprire l'oggetto file che utilizzarlo come gestore di contesto in un'unica espressione:
with open(filename) as open_file:
file_contents = open_file.read()
Scrivi il tuo gestore di contesto
Un gestore di contesto è un qualsiasi oggetto che implementa due metodi magici __enter__()
e __exit__()
(sebbene possa implementare anche altri metodi):
class AContextManager():
def __enter__(self):
print("Entered")
# optionally return an object
return "A-instance"
def __exit__(self, exc_type, exc_value, traceback):
print("Exited" + (" (with an exception)" if exc_type else ""))
# return True if you want to suppress the exception
Se il contesto esce con un'eccezione, le informazioni su exc_type
verranno passate come triplo exc_type
, exc_value
, traceback
(queste sono le stesse variabili restituite dalla funzione sys.exc_info()
). Se il contesto esce normalmente, tutti e tre questi argomenti saranno None
.
Se si verifica un'eccezione e viene passata al metodo __exit__
, il metodo può restituire True
per sopprimere l'eccezione, altrimenti l'eccezione verrà __exit__
alla fine della funzione __exit__
.
with AContextManager() as a:
print("a is %r" % a)
# Entered
# a is 'A-instance'
# Exited
with AContextManager() as a:
print("a is %d" % a)
# Entered
# Exited (with an exception)
# Traceback (most recent call last):
# File "<stdin>", line 2, in <module>
# TypeError: %d format: a number is required, not str
Si noti che nel secondo esempio anche se si verifica un'eccezione nel mezzo del corpo __exit__
with, il gestore __exit__
viene ancora eseguito, prima che l'eccezione si propaga all'ambito esterno.
Se hai solo bisogno di un metodo __exit__
, puoi restituire l'istanza del gestore di contesto:
class MyContextManager:
def __enter__(self):
return self
def __exit__(self):
print('something')
Scrivere il proprio contextmanager usando la sintassi del generatore
È anche possibile scrivere un gestore di contesto usando la sintassi del generatore grazie al decoratore contextlib.contextmanager
:
import contextlib
@contextlib.contextmanager
def context_manager(num):
print('Enter')
yield num + 1
print('Exit')
with context_manager(2) as cm:
# the following instructions are run when the 'yield' point of the context
# manager is reached.
# 'cm' will have the value that was yielded
print('Right in the middle with cm = {}'.format(cm))
produce:
Enter
Right in the middle with cm = 3
Exit
Il decoratore semplifica il compito di scrivere un gestore di contesto convertendo un generatore in uno. Tutto prima che l'espressione yield diventi il metodo __enter__
, il valore restituito diventa il valore restituito dal generatore (che può essere associato a una variabile __exit__
with) e tutto ciò che __exit__
l'espressione yield diventa il metodo __exit__
.
Se un'eccezione deve essere gestita dal contesto manager, un try..except..finally
-block può essere scritto nel generatore e qualsiasi eccezione sollevata nel with
-block saranno trattati da questo blocco eccezione.
@contextlib.contextmanager
def error_handling_context_manager(num):
print("Enter")
try:
yield num + 1
except ZeroDivisionError:
print("Caught error")
finally:
print("Cleaning up")
print("Exit")
with error_handling_context_manager(-1) as cm:
print("Dividing by cm = {}".format(cm))
print(2 / cm)
Questo produce:
Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit
Più gestori di contesto
Puoi aprire diversi gestori di contenuti allo stesso tempo:
with open(input_path) as input_file, open(output_path, 'w') as output_file:
# do something with both files.
# e.g. copy the contents of input_file into output_file
for line in input_file:
output_file.write(line + '\n')
Ha lo stesso effetto dei gestori di contesto di nesting:
with open(input_path) as input_file:
with open(output_path, 'w') as output_file:
for line in input_file:
output_file.write(line + '\n')
Gestisci risorse
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.open_file = open(self.filename, self.mode)
return self.open_file
def __exit__(self, *args):
self.open_file.close()
__init__()
metodo __init__()
imposta l'oggetto, in questo caso settando il nome del file e la modalità per aprire il file. __enter__()
apre e restituisce il file e __exit__()
lo chiude.
L'utilizzo di questi metodi magici ( __enter__
, __exit__
) consente di implementare oggetti che possono essere utilizzati facilmente with
l'istruzione with.
Usa la classe File:
for _ in range(10000):
with File('foo.txt', 'w') as f:
f.write('foo')