Utilizzo dell'istruzione "with" per i file CSV in Python
-
22-07-2019 - |
Domanda
È possibile utilizzare l'istruzione con
direttamente con i file CSV? Sembra naturale poter fare qualcosa del genere:
import csv
with csv.reader(open("myfile.csv")) as reader:
# do things with reader
Ma csv.reader non fornisce i metodi __enter__
e __exit__
, quindi non funziona. Posso tuttavia farlo in due passaggi:
import csv
with open("myfile.csv") as f:
reader = csv.reader(f)
# do things with reader
Questo secondo modo è il modo ideale per farlo? Perché non dovrebbero rendere csv.reader direttamente compatibile con la dichiarazione with?
Soluzione
L'uso principale dell'istruzione con
è una pulizia sicura delle eccezioni di un oggetto utilizzato nell'istruzione. con
si assicura che i file vengano chiusi, i blocchi vengano rilasciati, i contesti ripristinati, ecc.
csv.reader ha cose da pulire in caso di eccezione?
Vorrei andare con:
with open("myfile.csv") as f:
for row in csv.reader(f):
# process row
Non è necessario inviare la patch per utilizzare csv.reader
e con
insieme.
import contextlib
Aiuto sul gestore delle funzioni nel modulo contextlib :
contextmanager(func)
@contextmanager decorator.
Utilizzo tipico:
@contextmanager
def some_generator(<arguments>):
<setup>
try:
yield <value>
finally:
<cleanup>
Questo rende questo:
with some_generator(<arguments>) as <variable>:
<body>
equivalente a questo:
<setup>
try:
<variable> = <value>
<body>
finally:
<cleanup>
Ecco un esempio concreto di come l'ho usato: curses_screen .
Altri suggerimenti
Sì. Il secondo modo è corretto.
Quanto al perché? Chi lo sa mai. Hai ragione, probabilmente è un cambiamento facile. Non è la massima priorità di altre cose.
Puoi facilmente creare il tuo kit di patch e inviarlo.
Il problema è che csv.reader non gestisce realmente un contesto. Può accettare qualsiasi iterabile, non solo un file. Pertanto, non chiama close sul suo input (per inciso, se fosse possibile, potresti usare contextlib.closing). Quindi non è ovvio quale supporto contestuale per csv.reader farebbe effettivamente.
import csv
class CSV(object):
def __init__(self,path,mode):
self.path = path
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.path,self.mode)
if self.mode == 'r':
return csv.reader(self.file)
elif self.mode == 'w':
return csv.writer(self.file)
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
with CSV('data.csv','r') as reader:
for row in reader:
print row
È facile creare ciò che desideri utilizzando una funzione di generatore:
import csv
from contextlib import contextmanager
@contextmanager
def opencsv(path):
yield csv.reader(open(path))
with opencsv("myfile.csv") as reader:
# do stuff with your csvreader