在生成器超出范围时清理生成器的优雅机制?
Elegant mechanism to clean up a generator as it goes out of scope?
我在堆队列中使用多个生成器来循环访问磁盘上已排序的文件。通常 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
处理程序,但它在我的情况下不起作用,因为此代码将用于较长的 运行 过程。
当生成器超出范围并被删除时,它们 generator.close()
method is called, which in turn raises a 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”。
您可以从函数中创建上下文管理器来处理任何清理任务。
这里有一个简单的例子来说明我的意思:
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
我在堆队列中使用多个生成器来循环访问磁盘上已排序的文件。通常 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
处理程序,但它在我的情况下不起作用,因为此代码将用于较长的 运行 过程。
当生成器超出范围并被删除时,它们 generator.close()
method is called, which in turn raises a 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”。
您可以从函数中创建上下文管理器来处理任何清理任务。
这里有一个简单的例子来说明我的意思:
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