Подтвердить что ты не робот

Какой самый pythonic способ перебрать все строки из нескольких файлов?

Я хочу обрабатывать много файлов, как если бы они были всего одним файлом. Какой правильный питонический способ взять [filenames] = > [file objects] = > [lines] с генераторами/не считывать весь файл в память?

Мы все знаем, как правильно открыть файл:

with open("auth.log", "rb") as f:
    print sum(f.readlines())

И мы знаем правильный способ связать несколько итераторов/генераторов в один длинный:

>>> list(itertools.chain(range(3), range(3)))
[0, 1, 2, 0, 1, 2]

но как связать несколько файлов вместе и сохранить менеджеров контекста?

with open("auth.log", "rb") as f0:
    with open("auth.log.1", "rb") as f1:
        for line in itertools.chain(f0, f1):
            do_stuff_with(line)

    # f1 is now closed
# f0 is now closed
# gross

Я мог бы игнорировать менеджеров контекста и делать что-то подобное, но это не кажется правильным:

files = itertools.chain(*(open(f, "rb") for f in file_names))
for line in files:
    do_stuff_with(line)

Или это то, что Async IO - PEP 3156, и мне просто нужно подождать элегантного синтаксиса позже?

4b9b3361

Ответ 1

Всегда fileinput.

for line in fileinput.input(filenames):
    ...

Считывая источник, похоже, что fileinput.FileInput не может использоваться в качестве менеджера контекста 1. Чтобы исправить это, вы можете использовать contextlib.closing, поскольку экземпляры fileinput имеют надежный реализованный метод close:

from contextlib import closing
with closing(fileinput.input(filenames)) as line_iter:
    for line in line_iter:
        ...

Альтернативой диспетчеру контекста является запись простой функции, зависающей над файлами и возвращающей строки:

def fileinput(files):
    for f in files:
        with open(f,'r') as fin:
            for line in fin:
                yield line

Нет реальной потребности в itertools.chain здесь IMHO... Магия здесь находится в инструкции yield, которая используется для преобразования обычной функции в фантастически ленивый генератор.


1 В стороне, начиная с python3.2, fileinput.FileInput реализуется как менеджер контекста, который делает именно то, что мы делали раньше, с помощью contextlib. Теперь наш пример:

# Python 3.2+ version
with fileinput.input(filenames) as line_iter:
    for line in line_iter:
        ...

хотя другой пример будет работать и на python3.2 +.