如何使用“with open”语句打开多个文件(事先未知的文件数)?

时间:2014-02-10 14:57:36

标签: python file python-2.7 merge with-statement

我特别需要使用with open语句来打开文件,因为我需要一起打开几百个文件并使用K-way merge合并它们。我理解,理想情况下我应该保持K低,但我没有预见到这个问题。

从头开始不是一个选项,因为我有一个截止日期。所以在这一点上,我需要非常快速的I / O,它不会将整个/大部分文件存储在内存中(因为有数百个文件,每个大约10MB)。我只需要一次读取一行进行K-way合并。减少内存使用量是我现在的主要关注点。

我了解到with open是最有效的技术,但我无法理解如何在单个open语句中将with open所有文件放在一起。请原谅我的初学者无知!

更新:此问题已解决。事实证明问题不在于我是如何打开文件的。我发现过多的内存使用是由于垃圾收集效率低下造成的。我根本没有使用with open。我使用了常规f=open()f.close()。垃圾收集节省了一天。

3 个答案:

答案 0 :(得分:3)

通过使用内置的contextmanger函数装饰器来定义“with语句上下文管理器的工厂函数”,编写自己的上下文管理器来处理这个问题相当容易,正如文档所述。例如:

from contextlib import contextmanager

@contextmanager
def multi_file_manager(files, mode='rt'):
    """ Open multiple files and make sure they all get closed. """
    files = [open(file, mode) for file in files]
    yield files
    for file in files:
        file.close()

filenames = 'file1', 'file2', 'file3'

with multi_file_manager(filenames) as files:
    a = files[0].readline()
    b = files[2].readline()
        ...

答案 1 :(得分:1)

with open(...) as f: 
    # do stuff 

粗略翻译为

f = open(...)
# do stuff
f.close()

在您的情况下,我不会使用with open语法。如果你有一个文件名列表,那就做这样的事情

filenames = os.listdir(file_directory)
open_files = map(open, filenames)
# do stuff
for f in open_files:
    f.close()

如果您真的想使用with open语法,可以创建自己的上下文管理器,接受文件名列表

class MultipleFileManager(object):
    def __init__(self, files):
        self.files = files

    def __enter__(self):
        self.open_files = map(open, self.files)
        return self.open_files

    def __exit__(self):
        for f in self.open_files:
            f.close()

然后像这样使用它:

filenames = os.listdir(file_directory)
with MulitpleFileManager(filenames) as files:
    for f in files:
        # do stuff

在这种情况下,我看到使用上下文管理器的唯一优势是您不能忘记关闭文件。但手动关闭文件没有任何问题。请记住,当程序退出时,操作系统将回收其资源。

答案 2 :(得分:1)

虽然不是2.7的解决方案,但我应该注意到3.3 +,contextlib.ExitStack有一个很好的,正确的解决方案,可以用来正确地 (令人惊讶地难以获得)当你滚动自己的时候,并且很好:

from contextlib import ExitStack

with open('source_dataset.txt') as src_file, ExitStack() as stack:
    files = [stack.enter_context(open(fname, 'w')) for fname in fname_list]
    ... do stuff with src_file and the values in files ...
... src_file and all elements in stack cleaned up on block exit ...

重要的是,如果open中的任何一个失败,那么在该点之前成功的所有open将被确定地清除;在这种情况下,最天真的解决方案最终无法清理,最多依赖于垃圾收集器,以及在没有收集对象的锁定获取的情况下,无法释放锁定。

发布在此处,因为此问题被标记为未指定Python版本的duplicate的“原始”。