仅在迭代序列的一部分之后释放生成器资源

时间:2013-06-18 08:05:53

标签: python python-2.7 garbage-collection generator

我想逐行解析一个字符串,并为每个解析的结果提供一个生成器。迭代这些结果的代码可以选择不迭代整个序列,如果它找到了它想要的信息:

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

我认为,如果sstream变为condition(bar),这将使True保持打开状态。当我们完成对sstream的迭代时,保证foo()将被关闭的最佳方法是什么?我是否必须将生成器包装在类定义中并实现__del__?或者我可以依赖垃圾收集吗?我打算给foo()打电话以获取很多不同的字符串。

2 个答案:

答案 0 :(得分:2)

  

保证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。

答案 1 :(得分:1)

编辑:不要忘记下面破碎的废话;据我所知,你需要在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