在以下代码中,我假设我有两个生成排序和可比较值的生成器,并且我想制作一个生成器,生成两个生成的“同步”对。所谓同步,是指当它们产生相同的值时从两者中产生收益,否则仅使“延迟的”收益(将其产生的收益与None
配对)。
from itertools import repeat
def generate_pairs(g1, g2):
try:
n1 = next(g1)
except StopIteration:
yield from zip(repeat(None), g2)
# A
# raise StopIteration
try:
n2 = next(g2)
except StopIteration:
yield from zip(g1, repeat(None))
# A
# raise StopIteration
while True:
if n1 > n2:
yield (None, n2)
try:
n2 = next(g2)
except StopIteration:
yield (n1, None)
yield from zip(g1, repeat(None))
# B
# raise StopIteration
elif n1 < n2:
yield (n1, None)
try:
n1 = next(g1)
except StopIteration:
yield (None, n2)
yield from zip(repeat(None), g2)
# B
# raise StopIteration
else:
yield (n1, n2)
try:
n1 = next(g1)
except StopIteration:
yield from zip(repeat(None), g2)
# C
# raise StopIteration
try:
n2 = next(g2)
except StopIteration:
yield from zip(g1, repeat(None))
# C
# raise StopIteration
我应该在哪里明确提出StopIteration
?
在上述状态下,当我尝试使用已经同步的生成器时,我发现在C情况下需要加注。
pairs = generate_pairs((n1 for n1 in [1, 2, 3]), (n2 for n2 in [1, 2, 3]))
以上内容可以永远产生最后一对(3, 3)
:
from cytoolz import take
list(take(10, pairs))
输出:
[(1, 1),
(2, 2),
(3, 3),
(3, 3),
(3, 3),
(3, 3),
(3, 3),
(3, 3),
(3, 3),
(3, 3)]
在B中,似乎也应该提出一本手册StopIteration
:
pairs = generate_pairs((n1 for n1 in [1, 3]), (n2 for n2 in [1, 2]))
list(take(10, pairs))
输出:
[(1, 1),
(None, 2),
(3, None),
(None, 2),
(3, None),
(None, 2),
(3, None),
(None, 2),
(3, None),
(None, 2)]
从下面的测试中,在我看来,在A处也需要某种终止生成器的方法:
pairs = generate_pairs((_ for _ in []), (n2 for n2 in [1, 2, 3]))
list(take(10, pairs))
输出:
UnboundLocalError Traceback (most recent call last)
<ipython-input-96-61eb4df81d52> in <module>
----> 1 list(take(10, pairs))
<string> in generate_pairs(g1, g2)
UnboundLocalError: local variable 'n1' referenced before assignment
但是,如果我取消注释代码中的所有raise StopIteration
,则需要手动处理产生的异常。例如,不会在for循环中自动处理它们。
我希望我的成对生成器在两个输入生成器都用完之后就停止生成东西,而不会产生戏剧性。我怎么了?
似乎使用return
而不是raise StopIteration
可以很好地修复我的代码。不过,我仍然对一些解释感兴趣。