【问题标题】: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
      

      【讨论】:

        猜你喜欢
        • 2017-09-03
        • 2011-10-02
        • 2018-05-16
        • 1970-01-01
        • 1970-01-01
        • 2023-02-07
        • 2014-10-23
        • 2014-02-05
        • 1970-01-01
        相关资源
        最近更新 更多