【问题标题】:Cleanup of iterators that have not been fully exhausted清理未完全耗尽的迭代器
【发布时间】:2017-05-02 19:46:45
【问题描述】:

生成器的主要用途是处理存储在远程服务器上的 CSV 文件行。它允许我拥有一致的接口来线性处理存储在其中的数据。

现在,我使用 paramiko 来访问存储文件的 SFTP 服务器 - 如果您没有关闭文件本身,paramiko 有一个未正确关闭连接的突出问题。

我有一个访问 sftp 上单个文件的简单界面(这显然是一个伪代码 - 我省略了连接错误处理代码等)。

 def sftp_read_file(filename):
       with paramiko.open(filename) as file_obj:
          for item in csv.reader(file_obj):
               yield item

 def csv_append_column(iter_obj, col_name, col_val):
     # header
     yield next(iter_obj) + (col_name, )
     for item in iter_obj:
         yield item + (col_val, )

假设我想通过对有限行数运行脚本来测试对文件所做的一组转换:

def main():
    for i, item in enumerate(csv_append_column(sftp_read_file('sftp://...'), 'A', 'B')):
        print(item)
        if i > 0 and i % 100 == 0:
            break

脚本将退出,但如果没有 SIGINT,解释器将永远不会终止。我有哪些可能的解决方案?

【问题讨论】:

  • do from __future__ import generator_stop 可能是引起问题的 StopIteration 的传播,请参阅 PEP 479... 没关系,我想我不明白你的问题。
  • 你试过只调用 sys.exit() 吗?
  • @pvg 当然可以。问题不在于“如何使其工作”,而是如何正确释放不再使用的生成器,因为在我的情况下它们似乎没有被正确地垃圾收集。
  • @TadhgMcDonald-Jensen 不错。我认为您确实没有这样做,但 StopIteration 的传播是我在调试时必须克服的问题之一 - 感谢您的指点。
  • 如果您保留对需要关闭的生成器的引用,它有一个关闭生成器的.close() 方法,无论它是否进行垃圾收集,它都会退出with 块生成器。

标签: python python-3.x generator python-3.5 paramiko


【解决方案1】:

这不是最优雅的解决方案,但也许我们可以通过将生成器包装在一个对象中来构建 @tadhg-mcdonald-jensen 的建议:

class Stoppable(object):
    def __init__(self, fn):
        self.generator = fn

    def __enter__(self):
        return self.generator

    def __exit__(self, type_, value, traceback):
        self.generator.close()

然后像这样使用它:

def main():
    with Stoppable(sftp_read_file('sftp://...')) as reader:
        for i, item in enumerate(csv_append_column(reader, 'A', 'B')):
            print(item)
            if i > 0 and i % 100 == 0:
                break   

或者,如果我们不使用生成器方法进行流式传输,我们可以只包装生成器本身:

def stopit(fn):
    rg = [ x for x in fn ]
    for x in rg:
        yield x

现在我们可以这样称呼它:

def main():
    for i, item in enumerate(csv_append_column(stopit(sftp_read_file('...')), 'A', 'B')):
        print(item)
        if i > 0 and i % 100 == 0:
            break   

这将确保 with 块退出并且paramiko 关闭 sftp 连接,但代价是一次将所有行读入内存。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多