【发布时间】:2014-08-23 23:22:46
【问题描述】:
我有一段代码使用生成器从两个大文件中读取,并在两个文件之一中达到 EOF 时停止。我想知道(1)哪个生成器首先到达 EOF,(2)每个生成器的进度,即当第一个生成器到达 EOF 时,生成器中 i 的值(参见下面的代码),以及(3)另一个生成器中剩余的行数。我提前不知道每个文件有多长,并希望避免预先扫描文件。
我知道我可以通过以下方式获得进度:
每次调用
next()时增加一个计数器(这很丑!),或者让生成器返回一个计数器(见代码中的
counter1和counter2),
但在这两种情况下,我都不知道gen1 或gen2 中的哪一个达到了EOF。
我还发现我可以在StopIteration 异常中添加“消息”,但我想知道是否有更好的方法。在第一个 try...except 块之后,我能以某种方式找出哪个尚未达到 EOF 并推进它吗? (我尝试在生成器上使用close() 或throw(),或者在生成器中使用finally 子句,但并没有真正理解它们。)
def gen1(fp):
for i, line in enumerate(fp):
int_val = process_line(line)
yield int_val, i
raise StopIteration, ("gen1", i)
def gen2(fp):
for i, line in enumerate(fp):
float_val = process_line_some_other_way(line)
yield float_val, i
raise StopIteration, ("gen2", i)
g1 = gen1(open('large_file', 'r'))
g2 = gen2(open('another_large_file', 'r'))
try:
val1, counter1 = next(g1)
val2, counter2 = next(g2)
progress += 1
while True: # actual code is a bit more complicated than shown here
while val1 > val2:
val2, counter2 = next(g2)
while val1 < val2:
val1, counter1 = next(g1)
if val1 == val2:
do_something()
val1, counter1 = next(g1)
val2, counter2 = next(g2)
except StopIteration as err:
first_gen_name, first_num_lines = err.args
gen1_finished_first = gen_name == 'gen1'
# Go through the rest of the other generator to get the total number of lines
the_remaining_generator = g2 if gen1_finished_first else g1
try:
while True:
next(the_remaining_generator)
except StopIteration as err:
second_gen_name, second_num_lines = err.args
if gen1_finished_first:
print 'gen1 finished first, it had {} lines.'.format(first_num_lines) # same as `counter1`
print 'gen2 was at line {} when gen1 finished.'.format(counter2)
print 'gen2 had {} lines total.'.format(second_num_lines)
else:
... # omitted
【问题讨论】:
-
你必须使用生成器吗?如果没有,您可以定义一个可迭代的类,它知道如何从文件返回下一行,跟踪相对于总文件大小消耗的字节数,跟踪打开的文件句柄是否已用尽,以及因此可以随时报告进度。
-
不要在
next时增加计数器或在生成器中构建计数器,为什么不只是enumerate生成器? -
@user2357112 我不确定我理解你的意思。请注意,我在
while循环内的多行上推进(调用next)生成器。 -
@obk:您可以将生成器包装在
enumerate迭代器中,然后只需next即可。对于没有内置计数器的gen1,它看起来像g1 = enumerate(gen1(whatever)),然后是i, val = next(g1)。
标签: python