在发电机超出范围时清理发电机的优雅机制?

时间:2015-08-28 18:01:32

标签: python generator file-management

我在堆队列中使用多个生成器来遍历磁盘上的已排序文件。通常,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处理程序,但它在我的情况下不起作用,因为这个代码将在长时间运行的进程中使用。

2 个答案:

答案 0 :(得分:5)

当生成器超出范围并被删除时,会调用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:,那么该块适用于任何引发的异常,而不会在生成器自然结束时捕获(因为你没有 来处理`GeneratorExit')。

答案 1 :(得分:1)

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

这是我的意思的一个简单例子:

0
1
2
3
4
5
cleaning up

输出:

    $sqltopics = "SELECT topic_id, topic_subject, topic_date, topic_cat FROM `forum_topics` WHERE topic_id =  ". mysqli_real_escape_string($conn, $_GET['id']) or die(mysqli_error()); 
$resulttopics = $conn->query($sqltopics);        
while($rowtopics = $resulttopics->fetch_array(MYSQLI_ASSOC))
{
$topicname = $rowtopics['topic_subject'];
$topicid = $rowtopics['topic_id'];
}

$sqlposts = "SELECT post_id, post_content, post_date, post_by FROM `forum_posts` WHERE post_topic = ". mysqli_real_escape_string($conn, $_GET['id']) or die(mysqli_error()); ; 
$resultposts = $conn->query($sqlposts);        
$row_cntposts = $resultposts->num_rows;

               <form method="post" action="test.php?id=">
        <input type="text" name="reply-content" maxlength="255" size="255" ><br />
        <input type="submit" value="Submit reply" />
        </form>