2009-01-13 15 views
17

¿Es posible utilizar la instrucción with directamente con archivos CSV? Parece natural que ser capaz de hacer algo como esto:"con" declaración de archivos CSV en Python

import csv 
with csv.reader(open("myfile.csv")) as reader: 
    # do things with reader 

Pero csv.reader no proporciona los métodos y __enter____exit__, así 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 la segunda forma la forma ideal de hacerlo? ¿Por qué no harían que csv.reader sea directamente compatible con la declaración con?

+0

Como se menciona a continuación, realmente no tiene sentido para un lector de csv. ¡Pero sí lo hace para un escritor! – b0fh

Respuesta

18

El uso principal de with afirmación es una limpieza excepción de seguridad de un objeto que se utiliza en la declaración. with se asegura de que los archivos están cerrados, se liberan los bloqueos, se restauran los contextos, etc.

¿Tiene csv.reader cosas para limpiar en caso de excepción?

me gustaría ir con:

with open("myfile.csv") as f: 
    for row in csv.reader(f): 
     # process row 

No es necesario someter el programa a utilizar csv.reader y with comunicado conjunto.

import contextlib 

Ayuda en ContextManager función 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 esto:

with some_generator(<arguments>) as <variable>: 
     <body> 

equivalente a esto:

<setup> 
    try: 
     <variable> = <value> 
     <body> 
    finally: 
     <cleanup> 

Aquí está un ejemplo concreto de cómo lo he utilizado: curses_screen.

+2

@ J.F.Sebastion: Creo que todos los módulos de biblioteca "Compresión de datos" y "Formato de archivo" deberían ser compatibles directamente con. –

+0

@ S.Lott: acepto que la biblioteca estándar debe crear administradores de contexto en sí mismos, donde sea aplicable. En el caso del módulo 'csv', podría ser' reader = csv.open (ruta) 'pero * no *' reader = csv.reader (iterable) '. – jfs

+0

@ J.F. Sebastian +1 para una explicación detallada sobre cómo se puede usar contextlib para lograr esto, pero verifique la respuesta de @bluce para una implementación real para usar con csv. – technomalogical

3

Sí. La segunda forma es correcta.

cuanto a por qué? Quién sabe. Tienes razón, es probable que sea un cambio fácil. No es tan prioritario como otras cosas.

Puede hacer fácilmente su propio kit de parches y enviarlo.

+0

Bien, quizás lo haga. Gracias. – Kiv

+1

No estoy seguro de que esto es una "ofensa patchable".El lector CSV está destinado a actuar sobre un objeto de archivo abierto y proporcionar un conjunto de filas repetibles: no hay una adquisición y publicación real de recursos. Si desea salir rápidamente del bloque con, haga rows = list (csv.reader (file_)) y use filas fuera de él. – cdleary

+0

@cdleary: creo que la respuesta a with no tiene que reflejar el uso real de recursos, sino solo el de "recursos". Todos los módulos de biblioteca "Compresión de datos" y "Formato de archivo" deberían hacer esto para lograr una coherencia simple. –

0

Es fácil crear lo que desea 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 
+2

Eso no cierra el archivo en el caso de una excepción. Hace que la declaración 'with' sea válida ... pero eso es todo lo que hace. – kindall

2

El problema es csv.reader realidad no gestionar un contexto. Puede aceptar cualquier iterable, no solo un archivo. Por lo tanto, no llama estrecha en su entrada (por cierto si lo hiciera podría utilizar contextlib.closing). Por lo tanto, no es obvio qué soporte de contexto para csv.reader realmente haría.

1
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