我应该在什么时候使用python自定义生成器手动引发“StopIteration”?

2024-04-20 02:21:30 发布

您现在位置:Python中文网/ 问答频道 /正文

在下面的代码中,我假设有两个生成器生成排序的和可比较的值,我想生成一个生成器,从这两个生成器生成“同步”对。我所说的同步是指当它们产生相同的值时从两者中产生,否则只推进“延迟的”值(将它产生的值与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)]

从下面的测试来看,在我看来,某种结束发电机的方式也是必要的:

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很好地修复了我的代码。不过,我还是对一些解释感兴趣。你知道吗


Tags: infromnonefornextraiserepeatyield