Domanda

mi sono imbattuto la dichiarazione with Python per la prima volta oggi. Sto usando Python leggermente per diversi mesi e non sapevo nemmeno della sua esistenza! Dato il suo status di un po 'oscuro, ho pensato che sarebbe valsa la pena chiedere:

  1. Qual è la dichiarazione with Python progettato per essere utilizzato per?
  2. Cosa fare lo si utilizza per?
  3. Ci sono qualsiasi trucchi ho bisogno di essere a conoscenza di, o comuni anti-pattern associati il suo utilizzo? Eventuali casi in cui è meglio try..finally uso di with?
  4. Perché non è utilizzato più ampiamente?
  5. Quali classi della libreria standard sono compatibili con esso?
È stato utile?

Soluzione

  1. Credo che questo è già stato risposto da altri utenti prima di me, quindi ho solo aggiungo per ragioni di completezza: la dichiarazione with semplifica la gestione delle eccezioni incapsulando le attività di preparazione e di pulizia comuni nella cosiddetta I gestori di contesto . Maggiori dettagli possono essere trovati in PEP 343 . Per esempio, la dichiarazione open è un manager contesto in sé, che consente di aprire un file, tenerlo aperto fino a quando l'esecuzione è nel contesto della dichiarazione with in cui è stato utilizzato, e vicino non appena si lascia la contesto, non importa se hai lasciato a causa di un'eccezione o durante il flusso regolare controllo. La dichiarazione with può quindi essere utilizzato in modo simile alla RAII modello in C ++: una risorsa viene acquisita dalla dichiarazione with e rilasciato quando si lascia il contesto with.

  2. Alcuni esempi sono: file di apertura utilizzando with open(filename) as fp:, acquisendo serrature utilizzando with lock: (dove lock è un'istanza di threading.Lock). È anche possibile costruire il proprio manager di contesto utilizzando il decoratore contextmanager da contextlib. Per esempio, ho spesso usare questo quando devo cambiare la directory corrente temporaneamente per poi tornare al punto in cui ero:

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory
    

    Ecco un altro esempio che reindirizza temporaneamente sys.stdin, sys.stdout e sys.stderr a qualche altro file handle e ripristini in seguito:

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    E, infine, un altro esempio che crea una cartella temporanea e pulisce fino al momento di lasciare il contesto:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want
    

Altri suggerimenti

Vorrei suggerire due interessanti conferenze:

1. L'istruzione with viene usato per avvolgere l'esecuzione di un blocco con metodi definiti da un contesto gestore. Questo permette di modelli di utilizzo comune try...except...finally per essere incapsulati per un comodo riutilizzo.

2. Si potrebbe fare qualcosa di simile:

with open("foo.txt") as foo_file:
    data = foo_file.read()

o

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

O (Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

o

lock = threading.Lock()
with lock:
    # Critical section of code

3.  Non vedo alcun antipattern qui.
Citando Dive Into Python :

  

try..finally è buono. con è meglio.

4. Credo che è legato all'abitudine dei programmatori di istruzione use try..catch..finally da altre lingue.

La dichiarazione with Python è il supporto incorporato lingua del Resource Acquisition Is Initialization linguaggio comunemente usato in C ++ . Esso è destinato a consentire l'acquisizione sicura e il rilascio di operare risorse di sistema.

La dichiarazione with crea le risorse all'interno di un ambito / blocco. Si scrive il codice utilizzando le risorse all'interno del blocco. Quando il blocco esce le risorse vengono rilasciate in modo pulito indipendentemente dal risultato del codice nel blocco (cioè se le uscite dei blocchi normalmente oa causa di un'eccezione).

Molte risorse nella libreria Python che obbedire al protocollo richiesto dalla dichiarazione with e così può usato con esso out-of-the-box. Tuttavia chiunque può rendere le risorse che possono essere utilizzate in una con la dichiarazione mediante l'attuazione del protocollo di ben documentato: PEP 0343

Usa ogni volta che si acquista le risorse nell'applicazione che devono essere esplicitamente devolvibili come i file, connessioni di rete, serrature e simili.

Un esempio di antipattern potrebbe essere quella di utilizzare il with all'interno di un ciclo quando sarebbe più efficiente avere la with all'esterno del ciclo

per esempio

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

vs

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

Il primo modo è aprire e chiudere il file per ogni row che possono causare problemi di prestazioni rispetto al secondo modo con apre e chiude il file solo una volta.

Anche in questo caso per completezza io aggiungo la mia più utile caso d'uso per le dichiarazioni with.

I fare un sacco di calcolo scientifico e per alcune attività ho bisogno biblioteca Decimal per i calcoli a precisione arbitraria. Una parte del mio codice ho bisogno di alta precisione e per la maggior parte delle altre parti ho bisogno di meno precisione.

ho impostato la mia precisione di default per un numero basso e quindi utilizzare with per ottenere una risposta più precisa per alcune sezioni:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

Io uso questo molto con la prova ipergeometrica che richiede la divisione dei grandi numeri risultanti fattoriali forma. Quando si esegue il calcolo su scala genomica bisogna stare attenti a errori di arrotondamento e di trabocco.

PEP 343 - La 'con' affermazione , c'è un esempio sezione alla fine.

  

... nuova dichiarazione "con" al Python   linguaggio per make       E 'possibile fattore di fuori usi standard di try / finally dichiarazioni.

punti 1, 2, e 3 in ragionevolmente ben coperti:

4: è relativamente nuovo, disponibile solo in python2.6 + (o python2.5 usando from __future__ import with_statement)

L'istruzione with lavora con i cosiddetti manager di contesto:

http://docs.python.org/release/2.5 .2 / lib / typecontextmanager.html

L'idea è quella di semplificare la gestione delle eccezioni facendo la pulizia necessaria dopo aver lasciato la 'con' blocco. Alcuni del pitone built-in già opera come contesto manager.

Un altro esempio per il supporto out-of-the-box, e uno che potrebbe essere uno sconcertante po 'in un primo momento quando si è abituati al modo in cui built-in si comporta open(), sono oggetti connection di moduli di database popolari come:

Gli oggetti connection sono responsabili di contesto e come tali possono essere usate out-of-the-box in un with-statement, tuttavia quando si utilizza la suddetta nota che:

  

Quando il with-block è terminata, sia con un'eccezione o senza, la connessione non viene chiusa . Nel caso in cui le finiture with-block con un'eccezione, la transazione viene abortita, altrimenti la transazione si impegna.

Questo significa che il programmatore deve aver cura di chiudere la connessione stessi, ma permette di acquisire una connessione, e utilizzarlo in with-statements multiple, come mostrato nella psycopg2 docs :

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

Nell'esempio di cui sopra, si noterà che il cursor oggetti di psycopg2 sono anche responsabili di contesto. Dalla documentazione rilevante sul comportamento:

  

Quando un cursor esce dal with-block è chiuso, rilasciando qualsiasi risorsa eventualmente associato. Lo stato della transazione non è interessato.

In Python in genere “ ” istruzione viene utilizzata per aprire un file, elaborare i dati presenti nel file, e anche per chiudere il file senza chiamare un metodo close (). “Con” l'istruzione rende la gestione l'eccezione più semplice, fornendo attività di pulizia.

forma generale con:

with open(“file name”, “mode”) as file-var:
    processing statements

Nota: non c'è bisogno di chiudere il file chiamando close () su file var.close ()

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top