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


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow