【问题标题】:Freeing a generators resources after iterating only over a part of the sequence在仅迭代序列的一部分后释放生成器资源
【发布时间】:2013-06-18 08:05:52
【问题描述】:
我想逐行解析一个字符串,并为每个解析的结果提供一个生成器。迭代这些结果的代码如果找到它想要的信息,它可能会选择不迭代整个序列:
import StringIO
def foo(string):
sstream = StringIO.StringIO(string)
for line in sstream:
res = doSomethingWith(line)
yield res
sstream.close()
for bar in foo(mystring):
if condition(bar):
break
我认为,如果 condition(bar) 变为 True,这将使 sstream 保持打开状态。当我们完成对foo() 的迭代时,保证sstream 将被关闭的最佳方法是什么?我是否必须将生成器包装在类定义中并实现__del__?或者我可以在这里依靠垃圾收集吗?我打算致电foo() 获取很多不同的字符串。
【问题讨论】:
标签:
python
python-2.7
garbage-collection
generator
【解决方案1】:
保证sstream 将在何时关闭的最佳方法是什么?
我们已经完成了对 foo() 的迭代?
对于必须调用绝对的“清理”函数的一般情况,您可能必须在生成器之外调用它,例如...
from StringIO import StringIO
def foo(sstream):
for line in sstream:
res = doSomethingWith(line)
yield res
sio = StringIO(mystring)
try:
for bar in foo(sio):
if condition(bar):
break
finally:
sio.close()
上下文管理器似乎无法在生成器中工作,除非它们已经用尽了。比如……
from StringIO import StringIO
from contextlib import contextmanager
@contextmanager
def my_stringio(s):
print 'creating StringIO'
sio = StringIO(s)
yield sio
print 'calling close()'
sio.close()
def mygen():
with my_stringio('abcdefghij') as sio:
while 1:
char = sio.read(1)
if not char:
break
yield char
for char in mygen():
print char
if char == 'c':
break
...从不打印'calling close()'。
我是否必须将生成器包装在类定义中并实现
__del__?
这是另一种选择,但这种方法的问题在于,如果您设法用类实例创建循环引用,__del__ 方法将永远不会被调用。
或者我可以在这里依靠垃圾收集吗?
在这种情况下,你可以。
对于StringIO,调用close() 方法并不重要。您可能要确保的唯一一件事是它正在使用的内存已被垃圾收集,无论您的 for 循环终止的方式如何,都会发生这种情况 - 生成器将超出范围,其本地将是 GC' d.
【解决方案2】:
编辑:不要介意下面的废话;据我所知,您需要在 yield 所在的 for 循环中执行 break。
这样的工作可能吗?我很容易忽略一些东西。
import StringIO
# perform the break on the inner forloop first, to ensure sstream gets closed
break_ = false
def foo(string, break_):
sstream = StringIO.StringIO(string)
for line in sstream:
res = doSomethingWith(line)
if not break_: yield res
else: break
sstream.close()
for bar in foo(mystring, break_):
if break_:
break
elif condition(bar):
break_ = True