我有一个生成器功能,可以从文件中读取行并将其解析为对象。这些文件太大了,无法考虑将整个文件处理为一个列表,这就是为什么我使用生成器而不是列表的原因。
我很担心,因为在调用生成器时,我的代码有时会中断。如果找到了要查找的内容,则可以选择停止,然后再从文件中读取每个对象。我真的不了解废弃的生成器会发生什么,或更重要的是,我不知道打开的文件句柄会发生什么。
我想避免这里的资源泄漏。
示例代码:
def read_massive_file(file_path)
with open(file=file_path, mode='r', encoding='utf-8') as source_file:
for line in source_file:
yield parse_entry(line)
for entry in read_massive_file(my_file):
if is_the_entry_i_need(entry):
break
else:
# not found
pass
我的问题是:上面的代码会打开我的源文件,还是python找到关闭它的方法?
我从for
循环中消费的事实是否有任何改变?如果我在放弃迭代器之前为read_massive_file()
手动获得了一个迭代器并调用了next()
几次,我会看到相同的结果吗?
答案 0 :(得分:4)
这只会在CPython上迅速释放资源。要真正注意这种情况下的资源释放,您必须执行类似的操作
with contextlib.closing(read_massive_file(my_file)) as gen:
for entry in gen:
...
但我从未见过有人这样做。
当生成器被丢弃而没有完全耗尽时,生成器的__del__
方法将向生成器中抛出GeneratorExit
异常,以触发__exit__
方法和finally
块。在CPython上,这会在循环中断后立即发生,并且仅丢弃对生成器的引用,但是在其他实现上,例如PyPy,它可能仅在GC循环运行时发生,或者在GC不运行时根本不发生在程序结束之前。
GeneratorExit
将根据您的情况触发文件关闭。可能会意外捕获GeneratorExit
并继续执行,在这种情况下,可能不会触发适当的清除操作,但您的代码不会执行此操作。
答案 1 :(得分:0)
您永远不会保存read_massive_file
的返回值;唯一的引用由for
循环生成的代码在内部保存。一旦该循环完成,就应该对生成器进行垃圾收集。
如果你写的话会有所不同
foo = read_massive_file(my_file):
for entry in foo:
...
else:
...
现在,您必须等到foo
超出范围(或明确称为del foo
)之后才能收集生成器。