我想将许多文件视为一个文件。什么是适当的pythonic方式采取[filenames] => [文件对象] => [行]与生成器/没有将整个文件读入内存?
我们都知道打开文件的正确方法:
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的用途,我只需要等待优雅的语法?
答案 0 :(得分:21)
始终有fileinput
。
for line in fileinput.input(filenames):
...
但是,阅读source,似乎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
被实现为一个上下文管理器,它完全按照我们之前使用{{1 }}。现在我们的例子变成了:
contextlib
虽然另一个例子也适用于python3.2 +。