【问题标题】:Elegant mechanism to clean up a generator as it goes out of scope?当生成器超出范围时清理生成器的优雅机制?
【发布时间】:2015-11-23 11:27:16
【问题描述】:

我在堆队列中使用了几个生成器来遍历磁盘上的排序文件。通常 heapq 在超出范围之前不会完全耗尽,因此底层生成器永远不会达到 StopIteration 条件。

我希望能够将处理程序附加到生成器或其他一些优雅的机制,以便在生成器超出范围时删除磁盘上的文件。这些文件本身是临时的,因此可以删除它们。但是,如果它们没有被删除,程序最终会用临时文件填满磁盘。下面是生成器供参考:

def _read_score_index_from_disk(file_name, buffer_size=8*10000):
    """Generator to yield a float/int value from a file, does buffering
    and file managment to avoid keeping file open while function is not
    invoked"""

    file_buffer = ''
    file_offset = 0
    buffer_offset = 1

    while True:
        if buffer_offset > len(file_buffer):
            data_file = open(file_name, 'rb')
            data_file.seek(file_offset)
            file_buffer = data_file.read(buffer_size)
            data_file.close()
            file_offset += buffer_size
            buffer_offset = 0
        packed_score = file_buffer[buffer_offset:buffer_offset+8]
        buffer_offset += 8
        if not packed_score:
            break
        yield struct.unpack('fi', packed_score)

我知道atexit 处理程序,但在我的情况下它不起作用,因为此代码将用于长时间运行的进程。

【问题讨论】:

    标签: python generator file-management


    【解决方案1】:

    当生成器超出范围并被删除时,它们的generator.close() method 会被调用,这反过来会在你的生成器函数中引发GeneratorExit exception

    只需处理该异常:

    def _read_score_index_from_disk(file_name, buffer_size=8*10000):
        # ...
    
        try:
            # generator loop
        except GeneratorExit:
            # clean up after the generator
    

    如果您使用finally: 而不是except GeneratorExit:,则该块适用于引发的任何异常,而不会在生成器自然结束时捕获那些(因为您没有 em> 来处理 `GeneratorExit')。

    【讨论】:

    • 抱歉耽搁了!我发现自己再次在谷歌上搜索,并喜欢找到我从未标记为接受的我的问题和你的答案。我发现异常和finally 块都有效,但我认为我更喜欢finally,因为它涵盖了所有情况。
    【解决方案2】:

    您可以从一个函数中创建一个上下文管理器来处理任何清理任务。

    这里有一个简单的例子来说明我的意思:

    from contextlib import contextmanager
    
    def my_generator():
        for i in range(10):
            if i > 5:
                break
            yield i
    
    @contextmanager
    def generator_context():
        yield my_generator()
        print("cleaning up")
    
    with generator_context() as generator:
        for value in generator:
            print(value)
    

    输出:

    0
    1
    2
    3
    4
    5
    cleaning up
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-14
      • 2017-06-05
      • 2019-08-06
      • 2014-02-25
      相关资源
      最近更新 更多