Использование оператора «with» для файлов CSV в Python - PullRequest
19 голосов
/ 14 января 2009

Можно ли использовать оператор with напрямую с файлами CSV? Кажется естественным иметь возможность сделать что-то вроде этого:

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

Но csv.reader не предоставляет методы __enter__ и __exit__, поэтому это не работает. Однако я могу сделать это в два этапа:

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

Этот второй способ - идеальный способ сделать это? Почему они не сделали csv.reader напрямую совместимым с оператором with?

Ответы [ 5 ]

19 голосов
/ 14 января 2009

Основное использование оператора with - это безопасная для исключения очистка объекта, используемого в операторе. with обеспечивает закрытие файлов, снятие блокировок, восстановление контекстов и т. Д.

Есть ли у csv.reader какие-либо вещи для очистки в случае исключения?

Я бы пошел с:

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

Вам не нужно отправлять патч для совместного использования операторов csv.reader и with.

import contextlib

Справка по функции contextmanager в модуле contextlib :

contextmanager(func)
    @contextmanager decorator.

Типичное использование:

    @contextmanager
    def some_generator(<arguments>):
        <setup>
        try:
            yield <value>
        finally:
            <cleanup>

Это делает это:

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

эквивалентно этому:

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

Вот конкретный пример того, как я его использовал: curses_screen .

4 голосов
/ 14 января 2009

Да. Второй способ верен.

А почему? Кто нибудь знает. Вы правы, это, вероятно, легко изменить. Это не так важно, как другие вещи.

Вы можете легко сделать свой собственный набор патчей и отправить его.

2 голосов
/ 14 января 2009

Проблема в том, что csv.reader не управляет контекстом. Может принимать любой итеративный, а не только файл. Поэтому он не вызывает close на своем входе (кстати, если бы он это сделал, вы могли бы использовать contextlib.closing). Так что не совсем понятно, что в действительности делает контекстная поддержка csv.reader.

1 голос
/ 02 июня 2011
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
0 голосов
/ 14 января 2009

С помощью функции генератора легко создать то, что вы хотите:


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
...