2012-01-08 8 views
32

Ho ottenuto che il with dichiarazione aiuterà a trasformare questo:Come "with" è meglio di try/catch per aprire un file in Python?

try: 
    f = open(my_file) 
    do_stuff_that_fails() 
except: 
    pass 
finally: 
    f.close() 

Into:

with open(my_file) as f: 
    do_stuff_that_fails() 

Ma come è meglio? Hai ancora avuto modo di gestire il caso con il file non essere in grado di essere aperto (come si richiede all'utente di dirgli che non dispone di autorizzazioni), quindi in realtà si avrebbe:

try: 
    with open(my_file) as f: 
     do_stuff_that_fails() 
except (IOError, OSError, Failure) as e: 
    do_stuff_when_it_doesnt_work() 

Che è equivalente a:

try: 
    f = open(my_file) 
    do_stuff_that_fails() 
except (IOError, OSError, Faillure) as e: 
    do_stuff_when_it_doesnt_work() 
finally: 
    f.close() 

Sì, avete guadagnato due linee, ma è stato aggiunto un livello di nidificazione wich non lo rende più facile da leggere. Lo scopo dell'istruzione with è di salvarti due righe o mi manca qualcosa?

Sembra molto aggiungere una parola chiave solo per questo, quindi mi sento come se ci fosse qualche sintassi per gestire il tentativo aggiuntivo/tranne che non so.

+3

"with" sta per essere un * nome tag * terribile, soggetto a abuso newbie. Possiamo trovare qualcosa di meglio? – Charles

+0

+1 Che ne dici di with-keyword? –

+0

Python ha bisogno di un: trywith aprire ('qualunque', 'r') come f: .... eccezione (bla): .... – Will

risposta

29

Per cominciare, aiuta a prevenire il problema che hai introdotto nell'esempio .

Il modo in cui hai strutturato, se viene generata un'eccezione durante il tentativo di aprire il file allora non sarà mai associare un file aperto al nome f, portando sia ad un NameError nella clausola finally (se è mai f stato legato nell'ambito) o qualcosa del tutto inaspettato (se lo è).

la correttezza struttura (equivalente al with) è:

f = open(my_file) 

try: 
    do_stuff_that_fails() 
finally: 
    f.close() 

(nota - non necessita di una clausola except se hai niente da fare).

Il secondo esempio è allo stesso modo sbagliato, e dovrebbe essere strutturato come:

try: 
    f = open(my_file) 

    try: 
     do_stuff_that_fails() 
    except EXPECTED_EXCEPTION_TYPES as e: 
     do_stuff_when_it_doesnt_work() 
    finally: 
     f.close() 

except (IOError, OSError) as e: 
    do_other_stuff_when_it_we_have_file_IO_problems() 

Il secondo è (come indicato in un'altra risposta) che non si può dimenticare di chiamare f.close().

A proposito, il termine è "gestione del contesto", non "gestione delle risorse" - la dichiarazione with gestisce contesti, alcuni dei quali possono essere le risorse, ma altri no. Ad esempio, viene anche utilizzato con decimal per stabilire un contesto decimale per un particolare blocco di codice.

Infine (rispondendo al tuo commento alla risposta precedente) non dovresti mai fare affidamento sulla semantica del refcount per gestire le risorse in Python.Jython, IronPython e PyPy hanno tutti una semantica non-refcount e non c'è nulla che impedisca a CPython di andare nella direzione opposta (anche se è altamente improbabile per l'immediato futuro). In un ciclo stretto (ad esempio os.walk) è molto facile eseguire gli handle di file se il codice che si basa sulla semantica del refcount viene eseguito su una VM con un comportamento diverso.

+1

+1 Non mi sono neanche accorto di aver fatto un errore così stupido. Grazie per la segnalazione. –

6

E 'per la gestione delle risorse ... non per come si reagisce ad un'eccezione altrimenti :)

Non v'è alcun modo per "dimenticare" f.close() quando si utilizza with. In questo modo ha lo stesso ruolo di using in C#.

Felice codifica.

+0

quindi è davvero tutto di fare la cosa giusta wihtout dover Scrivilo. Mi piace il termine gestione delle risorse, ha senso. Ma creare una nuova parola chiave e un nuovo modo di pensare le operazioni di base per risparmiare un "finalmente" sembra eccessivo dato che la gestione delle risorse viene gestita automaticamente in distruttori chiamati a garbage collection. Che, a meno di scrivere blocchi giganteschi o un sacco di codici ricorsivi, avviene rapidamente alla fine della tua funzione o metodo. So che ** dovresti ** chiuderli esplicitamente, ma funziona se non lo fai. Alla fine, quanti casi d'uso traggono realmente beneficio da 'with'? –

+0

@ e-satis I distruttori sono * non necessariamente * deterministici in tutte le implementazioni, anche se CPython è stato basato storicamente sul conteggio dei ref-in (ma questo può ancora essere bonkerato con scope variabile, con 'with' viene gestito * now *). Non sono sicuro del motivo per cui è stata scelta la sintassi, ma io * amo * la funzione. Forse un ulteriore 'trywith (...):' costruire sarebbe stato pulito. –

+0

Esatto, si tratta di essere pitoni. Tu fai la cosa giusta, è ovvia, automatica, più corta e deterministica. Sto solo rendendo le cose un po 'più semplici, come i decoratori che sono solo zucchero sintetico. Solo per quel commento, accetterò la risposta. –

15

Nell'esempio che si fornisce, è non migliore. È consigliabile individuare le eccezioni più vicine al punto in cui vengono generate per evitare di rilevare eccezioni non correlate dello stesso tipo.

try: 
    file = open(...) 
except OpenErrors...: 
    # handle open exceptions 
else: 
    try: 
     # do stuff with file 
    finally: 
     file.close() 

come purtroppo verbose come questo è, il with statement non consente di cogliere le eccezioni sollevate nel corso della sua valutazione. C'era un suggestion per aggiungere la gestione delle eccezioni in tal senso sulla mailing list:

with open(...) as file: 
    # do stuff with file 
except OpenErrors...: 
    # handle open exceptions 

Ma questo era shot down.

infine la pena notare che è possibile inserire direttamente e manager di contesto uscire in questo modo:

file = open(...).__enter__() 
file.__exit__(typ, val, tb) 

Questo è descritto in modo più dettagliato e herehere.

Come linea guida generale, le istruzioni with sono eccellenti per i casi in cui non sono previste eccezioni e il comportamento predefinito "entra/apri/acquista" è adeguato. Gli esempi includono i file richiesti e il blocco semplice.

Problemi correlati