14

我想将许多文件视为一个文件。将 [filenames] => [file objects] => [lines] 与生成器/不将整个文件读入内存的正确 pythonic 方式是什么?

我们都知道打开文件的正确方法:

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的用途,我只需要稍后等待优雅的语法吗?

4

1 回答 1

21

总是有的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……这里的魔力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+。

于 2013-04-19T01:49:37.880 回答