【发布时间】:2017-06-12 09:53:56
【问题描述】:
使用上下文管理器自动关闭文件是python中的一个常见习语:
with open('filename') as my_file:
# do something with my_file
# my_file gets automatically closed after exiting 'with' block
现在我想读取几个文件的内容。数据的消费者不知道也不关心数据是来自文件还是非文件。它不想检查它收到的对象是否可以打开。它只是想从中读取一些内容。所以我创建了一个这样的迭代器:
def select_files():
"""Yields carefully selected and ready-to-read-from files"""
file_names = [.......]
for fname in file_names:
with open(fname) as my_open_file:
yield my_open_file
这个迭代器可以这样使用:
for file_obj in select_files():
for line in file_obj:
# do something useful
(请注意,相同的代码可用于使用的不是打开的文件,而是字符串列表 - 这很酷!)
问题是:产生打开的文件是否安全?
看起来像“为什么不呢?”。消费者调用迭代器,迭代器打开文件,将其交给消费者。消费者处理文件并返回到下一个迭代器。迭代器代码继续,我们退出 'with' 块,my_open_file 对象被关闭,转到下一个文件,等等。
但是如果消费者永远不会返回到下一个文件的迭代器怎么办? F.e.消费者内部发生异常。或者消费者在其中一个文件中发现了一些非常令人兴奋的东西,并高兴地将结果返回给调用它的人?
在这种情况下迭代器代码永远不会恢复,我们永远不会到达 'with' 块的末尾,my_open_file 对象永远不会关闭!
或者会吗?
【问题讨论】:
-
迭代器超出范围时将被清理,在您提到的情况下应该这样做。
-
如果您在消费者中保存对生成器的引用(例如,
producer=select_files()),那么您可以使用其.throw方法告诉它关闭。 docs.python.org/3/reference/expressions.html#generator.throw. -
@TerryJanReedy 生成器有一个
close方法,它更好地服务于停止生成器而不是在那里抛出随机异常... -
不管怎样,如果你简单地产生文件的内容,同样的问题会发生:
with open(...) as f: for line in f: yield line。消费者可能不会耗尽生成器,因此文件可能永远不会关闭。这通常是“惰性 I/O”的问题。最好在“eager”代码中打开文件并将它们传递给惰性函数。 -
虽然这不能直接解决 OP 的问题...处理这种情况的另一种方法是使用
fileinput。另见stackoverflow.com/questions/16095855/…
标签: python yield with-statement