什么时候应该使用python自定义生成器手动提高`StopIteration`?

时间:2019-10-10 14:16:59

标签: python exception generator

在以下代码中,我假设我有两个生成排序和可比较值的生成器,并且我想制作一个生成器,生成两个生成的“同步”对。所谓同步,是指当它们产生相同的值时从两者中产生收益,否则仅使“延迟的”收益(将其产生的收益与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可以很好地修复我的代码。不过,我仍然对一些解释感兴趣。

0 个答案:

没有答案