Usando & # 8220; con & # 8221; declaración para archivos CSV en Python
-
22-07-2019 - |
Pregunta
¿Es posible usar la instrucción with
directamente con archivos CSV? Parece natural poder hacer algo como esto:
import csv
with csv.reader(open("myfile.csv")) as reader:
# do things with reader
Pero csv.reader no proporciona los métodos __enter__
y __exit__
, por lo que esto no funciona. Sin embargo, puedo hacerlo en dos pasos:
import csv
with open("myfile.csv") as f:
reader = csv.reader(f)
# do things with reader
¿Es esta segunda forma la forma ideal de hacerlo? ¿Por qué no harían csv.reader directamente compatible con la declaración with?
Solución
El uso principal de la instrucción with
es una limpieza segura de excepciones de un objeto utilizado en la declaración. with
se asegura de que los archivos estén cerrados, se liberen bloqueos, se restauren los contextos, etc.
¿ csv.reader tiene cosas que limpiar en caso de excepción?
Yo iría con:
with open("myfile.csv") as f:
for row in csv.reader(f):
# process row
No necesita enviar el parche para usar csv.reader
y con
declaración juntos.
import contextlib
Ayuda sobre la función contextmanager en el módulo contextlib :
contextmanager(func)
@contextmanager decorator.
Uso típico:
@contextmanager
def some_generator(<arguments>):
<setup>
try:
yield <value>
finally:
<cleanup>
Esto hace que:
with some_generator(<arguments>) as <variable>:
<body>
equivalente a esto:
<setup>
try:
<variable> = <value>
<body>
finally:
<cleanup>
Aquí hay un ejemplo concreto de cómo lo he usado: curses_screen .
Otros consejos
Sí La segunda forma es correcta.
¿Por qué? Quien sabe Tienes razón, probablemente sea un cambio fácil. No es tan prioritario como otras cosas.
Puede hacer fácilmente su propio kit de parches y enviarlo.
El problema es que csv.reader no gestiona realmente un contexto. Puede aceptar cualquier iterable, no solo un archivo. Por lo tanto, no llama a close en su entrada (por cierto, si lo hiciera, podría usar contextlib.closing). Por lo tanto, no es obvio qué soporte contextual para csv.reader realmente haría.
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
Es fácil crear lo que quiere usando una función de generador:
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