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: BLOCKLa 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')