我特别需要使用with open
语句来打开文件,因为我需要一起打开几百个文件并使用K-way merge合并它们。我理解,理想情况下我应该保持K低,但我没有预见到这个问题。
从头开始不是一个选项,因为我有一个截止日期。所以在这一点上,我需要非常快速的I / O,它不会将整个/大部分文件存储在内存中(因为有数百个文件,每个大约10MB)。我只需要一次读取一行进行K-way合并。减少内存使用量是我现在的主要关注点。
我了解到with open
是最有效的技术,但我无法理解如何在单个open
语句中将with open
所有文件放在一起。请原谅我的初学者无知!
更新:此问题已解决。事实证明问题不在于我是如何打开文件的。我发现过多的内存使用是由于垃圾收集效率低下造成的。我根本没有使用with open
。我使用了常规f=open()
和f.close()
。垃圾收集节省了一天。
答案 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的“原始”。