2012-07-07 8 views
9

Non intendo semplicemente sprecare il tuo tempo, ma: ti è venuto in mente anche tu, mentre usando la dichiarazione with di Python è in realtà contrario alla quinta riga di "The Lo Zen di Python "che dice" Flat è meglio di nested "? Può qualche guru Python illuminato condividere le mie intuizioni su questo?zen di Python vs con affermazione - pondering filosofico

(Ho sempre trovato che un ulteriore livello di rientro si apre nel mio codice ogni volta che uso with invece di f.close() ... e non è che io non sono gonna uso try: ... finally: ... comunque e quindi i benefici di with ancora mi sfuggono, come io crescere ad apprezzare e capire Python sempre di più ...)


@glglgl (scusate, non riesco a trovare un modo per scrivere codice nei commenti): sì, ma se vai al modo with, il tuo codice diventa:

try: 
    with file(...) as f: 
     ... 
except IOError: 
    ... 

... e utilizzando solo con senza il try è quello che la gente finisce per fare il tipo di hacky "un uso" codice in cui usano f.close() invece che con ogni modo (il che è un male perché il file non può essere chiuso se un'eccezione viene lanciata prima del loro f.close()), quindi per il codice "hacky" le persone semplicemente non usano with perché, non lo so, immagino che lo trovino troppo "stravagante" e per un codice ben strutturato che non porta qualsiasi beneficio comunque, quindi mi sembra che non ci sia un vero e proprio caso d'uso per il mondo ... quello era il mio modo di ponderare davvero.

+1

La maggior parte delle volte lascio IOError propagarsi e catturarlo da qualche altra parte. – Dikei

+0

Per quanto non sia d'accordo con la premessa di questa domanda, devo darti degli oggetti di scena per farmi tornare indietro e rileggere PEP [342] (http: //www.python.org/dev/peps/pep-0342 /) e [343] (http://www.python.org/dev/peps/pep-0343/) – kojiro

risposta

5

Sì, The Zen of Python afferma "L'appartamento è migliore di quello annidato", tuttavia non è l'unica caratteristica che ci interessa; afferma anche "Semplice è meglio che complesso". La bellezza di with è che in realtà rispetta lo di entrambi i principi, come spiegherò di seguito.

Ogni volta che ti ritrovi a riflettere filosoficamente su una funzione in Python, probabilmente vale la pena consultare lo Python Enhancement Proposals (PEPs) per leggere la motivazione alla base della funzionalità. In questo caso PEP 343 -- The "with" Statement dice che in attacco in astratto:

Questa PEP aggiunge una nuova dichiarazione "con" al linguaggio Python per renderlo possibile scomporre usi standard di try/finally dichiarazioni.

Il factoring delle dichiarazioni try/finally semplifica e rende più leggibile il codice.

PEP 343 va più in profondità che fornire uno zucchero sintattico semplicistico, tuttavia. Si stabilisce un protocollo contesto manager:

L'espressione che segue immediatamente la con parola chiave nella dichiarazione è un "espressione contesto" come quella espressione fornisce l'indizio principale per l'ambiente di runtime manager contesto stabilisce per la durata del corpo della dichiarazione.

Utilizzando il protocollo di gestione del contesto, gli autori di API possono aiutare a nascondere la complessità e garantire la corretta acquisizione/rilascio di risorse in un contesto multi-thread.

Ma la vera bellezza della dichiarazione with è mostrato nell'esempio 12 del PEP 343 che spiega che:

Un manager contesto "annidata" che nidifica automaticamente i forniti contesti da sinistra a destra per evitare indentazione eccessiva.

Utilizzando il manager nested() contesto si può prendere il codice che assomiglia a questo:

with a as x: 
    with b as y: 
     with c as z: 
      # Perform operation 

e trasformarlo in questo:

with nested(a, b, c) as (x, y, z): 
      # Perform operation 

Nota che nested() è stato introdotto in Python 2.5, ma a partire dalla versione 2.7 è deprecato a favore di questo modulo sintattico di gestione contesto multiplo:

with a as x, b as y, c as z: 
      # Perform operation 

Chiaramente non solo è più semplice e più leggibile, ma è molto più piatto di quello annidato. Così, utilizzando with è seguendo il percorso di 無爲 :)

UPDATE: In risposta a comments on Simeon Visser's answer qui è un esempio di quando si potrebbe utilizzare più gestori di contesto di aprire più di un file alla volta, quando si desidera zip il contenuto di due (o più) file insieme tale che se l'apertura di uno dei file fallisce renderà il tutto fallire e chiudere correttamente ogni file che è stato aperto:

from itertools import izip 
with open("/etc/passwd") as a, open("/etc/group") as b, open("/etc/shadow") as c: 
    for lines in izip(a,b,c): 
     print map(lambda x: x.split(':')[0], lines) 

Esegui questo esempio due volte; una volta come root e una volta come utente normale. Supponendo di salvare questo file come ziptogether.py, provare prima a chiamarlo come root con sudo python ziptogether.py e ci riuscirà, ma il richiamo come utente normale con python ziptogether.py avrà esito negativo perché non si dispone delle autorizzazioni per leggere /etc/shadow. Quando fallisce, il gestore di contesto si assicura che i file che sono stati aperti correttamente prima dell'errore vengano chiusi correttamente quando l'esecuzione si sposta al di fuori dell'ambito dell'istruzione with.

+1

kudos. bella e risposta informativa! – NeuronQ

+1

@NeuronQ Ho aggiornato la mia risposta per notare che 'nested()' è deprecato e c'è una forma sintattica ancora più semplice per più gestori di contesto. – aculich

7

si parla già: è più pulito di fare

f = file(...) 
try: 
    # do work on file 
finally: 
    f.close() 

di una semplice chiusura dopo le operazioni di file - che non sarebbe stato raggiunto se si verifica un'eccezione.

Se si confronta lo try/finally in with, si ha lo stesso livello di indentazione, quindi non si perde nulla. Se, tuttavia, gestisci le eccezioni, hai un livello in più di indentazione, che è in effetti contrario al detto punto Zen.

OTOH, with incapsula le cose e rende più semplice e più leggibile il loro utilizzo, che sono altri aspetti Zen.

Mi sembra impossibile seguire sempre ogni aspetto Zen; a volte devi pesare l'uno contro l'altro. In questo caso, si "perde" un livello di indentazione, ma si ottiene una migliore leggibilità e manutenibilità. Quest'ultimo sembra essere un vantaggio per me.

+0

Ho provato a risponderti ma non ho trovato il modo di scrivere codice nei commenti così ho aggiunto la mia risposta a te in fondo alla mia domanda – NeuronQ

+0

@NeuronQ Ho aggiornato la risposta – glglgl

+0

Concordo: Elementi dello Zen di Python hanno spesso bisogno di essere scambiati uno contro l'altro. Nel caso in questione, direi che l'unico livello di nidificazione che devi investire ti permette di seguire ** Explicit è meglio di implicito **: quando vedi un 'with', è esplicito che si verificherà la gestione della fine (piuttosto che a seconda del destino dell'esecuzione della logica). "Explicit is better than implicit" è il numero 2 nella lista di Zen di Python e quindi * dovrebbe * sovrascrivere spesso meno annidamenti. Tutto bene. –

7

Si noti che lo Zen di Python dice anche:

Semplice è meglio che complesso.

Complesso è meglio che complicato.

e

conta leggibilità.

attraverso un gestore di contesto nella dichiarazione with fornisce molteplici cose:

  • corretto comportamento come il file viene sempre chiuso
  • leggibilità (with open(..) as f è abbastanza comprensibile)

Puoi punta a un elemento nello Zen di Python e sostiene che tutto il codice Python deve soddisfare tutti gli elementi in ogni momento. Ad esempio, se il livello minimo di indentazione per risolvere un particolare problema in modo leggibile e corretto è quattro, allora così sia: se un livello di rientro di tre rende il codice meno leggibile, lascia solo il codice (quattro è buono).

+0

Quello che dici è molto, molto generale ... Hai ragione sul punto "imporre il comportamento corretto", dato che sarebbe un bene per il codice hacky veloce, ma ho scoperto che esattamente le persone che scrivono "codice hacky veloce" usare la dichiarazione with. E la parte di leggibilità è soggettiva (trovo un altro livello di indentazione meno leggibile, e quando fai un "file multipli con" come "con open (...) come file1, apri (...) come file3, apri (. ..) come file4: "finisce molto illeggibile perché devi scorrere in orizzontale o brutto perché hai diviso l'istruzione con più righe. – NeuronQ

+0

C'è una buona ragione per aprire quattro file contemporaneamente? leggere i dati dai file di input in sequenza e lo stesso per scrivere i dati su uno o più file di output.Ma posso immaginare ci siano situazioni in cui sono necessari quattro file. –

+0

@NeuronQ: Non riesco a vedere nulla uggly quando si interrompe il comando su più linee, soprattutto quando le parti 'open (...) as fX' sono allineate.Sono d'accordo con Simeon non è il caso tipico.Tuttavia, è leggibile allo stesso modo di se i quattro consequenziali' fX = open (. ..) sono stati usati i comandi. – pepr

1
"Flat is better than nested" 

Ebbene, ciò che è piatta?

import thirdparty 
print "I Like Pie!" 

vs

import org.example.thirdparty.something 
System.out.println("I Like Cake") 

ecc ...

Lo Zen di Python non si limita a rispettare un limite di rientro sul vostro codice. Ti incoraggia a scrivere codice leggibile (e quindi migliore). Se l'istruzione with è in una funzione accessibile solo da 3 livelli di oggetti (ecc., one.two.three.func()), è un problema.

In caso contrario, tre livelli di indentazione sono altrettanto buoni di un numero qualsiasi.

1

Il motivo per preferire l'with è che non è necessario accoppiare manualmente le operazioni connesse (come open(...)/.close(), ma il costrutto with è più generale - non solo per lavorare con file). Questo è importante in particolare nei casi in cui la seconda operazione potrebbe non essere eseguita a causa dei motivi che non sono chiaramente visibili dal codice sorgente. Si sta dicendo alla macchina prendersi cura di me per me, e la macchina è migliore nel caso che umano. In questo modo ti libererai del gruppo di brutti errori che potrebbero essere difficili da trovare.

A proposito, è necessario utilizzare open(...) anziché file(...). Python 3 non sa nulla del file(...) e dovrai altrimenti correggere il tuo codice in un secondo momento.

+0

Infatti, forse dovrei esplorare l'uso di 'with' con altre cose oltre ai file, non ho mai veramente pensato a questo. Sì, 'open (...)' è quello che uso, non so dove fosse la mia mente quando ho scritto 'file (...)' ma fortunatamente non ha influenzato il significato della domanda. – NeuronQ