迭代多个文件的所有行的最pythonic方法是什么?

Con*_*ean 14 python logging parsing file

我想将许多文件视为一个文件.什么是正确的pythonic方式采用[filenames] => [文件对象] => [行]与生成器/不读取整个文件到内存?

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

with open("auth.log", "rb") as f:
    print sum(f.readlines())
Run Code Online (Sandbox Code Playgroud)

我们知道将几个迭代器/生成器链接到一个长迭代器的正确方法:

>>> list(itertools.chain(range(3), range(3)))
[0, 1, 2, 0, 1, 2]
Run Code Online (Sandbox Code Playgroud)

但是如何将多个文件链接在一起并保留上下文管理器?

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
Run Code Online (Sandbox Code Playgroud)

我可以忽略上下文管理器并执行类似的操作,但感觉不对:

files = itertools.chain(*(open(f, "rb") for f in file_names))
for line in files:
    do_stuff_with(line)
Run Code Online (Sandbox Code Playgroud)

或者这是Async IO-PEP 3156的用途,我只需要等待优雅的语法?

mgi*_*son 21

永远都有fileinput.

for line in fileinput.input(filenames):
    ...
Run Code Online (Sandbox Code Playgroud)

但是,阅读源代码似乎fileinput.FileInput不能用作上下文管理器1.要解决这个问题,你可以使用,contextlib.closing因为FileInput实例有一个明智的实现close方法:

from contextlib import closing
with closing(fileinput.input(filenames)) as line_iter:
    for line in line_iter:
        ...
Run Code Online (Sandbox Code Playgroud)

使用上下文管理器的另一种方法是编写一个简单的函数循环遍历文件并随时生成行:

def fileinput(files):
    for f in files:
        with open(f,'r') as fin:
            for line in fin:
                yield line
Run Code Online (Sandbox Code Playgroud)

没有真正的需要在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:
        ...
Run Code Online (Sandbox Code Playgroud)

虽然另一个例子也适用于python3.2 +.

  • 从Python 3.2开始,`fileinput`可以用作上下文管理器(http://docs.python.org/3/library/fileinput.html. (3认同)