【问题标题】:What's the most pythonic way to iterate over all the lines of multiple files?迭代多个文件的所有行的最pythonic方法是什么?
【发布时间】:2013-04-12 07:54:28
【问题描述】:

我想将许多文件视为一个文件。将 [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 的用途,我只需要稍后等待优雅的语法吗?

【问题讨论】:

  • 还要注意files = itertools.chain(*(open(f, "rb") for f in file_names)) 在这种情况下肯定不好。解压缩元组会导致在您实际进入 chain 构造函数之前打开所有文件。使用itertools.chain.from_iterable(open(fname,'r') for fname in filenames)) 会更好——事实上,这是from_iterable 类方法首先需要存在的一个经典原因:)。
  • @mgilson 不知道 from_iterable 是个东西!我很高兴我的用例是一个教科书示例,说明它为什么有用。我试图弄清楚如何在没有嵌套 for 循环的情况下正确地让惰性求值工作。谢谢!
  • 请注意,即使 from_iterable 也不能保证在您完成迭代后所有文件都已关闭,因为您永远不知道 __del__ 何时实际运行(尽管我很确定他们会在 Cpython 中)...
  • contextlib.ExitStack 允许将多个上下文管理器视为一个(在您的情况下不需要,但在相关情况下可能有用)。

标签: python file parsing logging


【解决方案1】:

总是有fileinput

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

然而,阅读sourcefileinput.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+。

【讨论】:

  • @Blender -- 这是一个不错的模块,不会被太多使用,因为它的功能可以被chain.from_iterable 替换。 itertoolscollections 是人​​们在 90% 的时间内使用的更知名的工具。我有点失望,它没有被实现为上下文管理器(它甚至不是一个新样式的类)。看起来这将是一个非常简单的添加,但幸运的是,它很容易用 contextlib 包装。
  • 从 Python 3.2 开始,fileinput 可以用作上下文管理器 (docs.python.org/3/library/fileinput.html.
  • @NedDeily -- 我很高兴他们在阅读源代码几分钟后实现了它the same way I did(在意识到这是一个旧式课程之前)
  • 我不确定上下文管理器是否甚至可以使用旧样式类...经过非常快速的测试,它们似乎可以...但是contextlib.closing 仍然比子类更好为此我认为...
  • @Conrad.Dean -- 足够合理。值得指出的是,fileinput 采用可选参数 openhook,而不是 open。因此,您只需要将文件的打开委托给 openhook 函数。
猜你喜欢
  • 2015-09-11
  • 2013-01-14
  • 2014-05-22
  • 1970-01-01
  • 2020-04-19
  • 1970-01-01
  • 2010-09-30
相关资源
最近更新 更多