一旦发电机列表中的发电机耗尽,继续使用其他发电机吗?

问题描述 投票:0回答:3

我在一个函数中有一个生成器列表

alternate_all(*args)
,它在列表中的每个生成器之间交替打印它们的第一个项目、第二个项目……等等,直到所有生成器都用完。

我的代码一直工作,直到生成器耗尽,一旦发生 StopIteration,它就会停止打印,当我希望它继续使用其余生成器并忽略耗尽的生成器时:

def alternate_all(*args):
    iter_list = []
    for iterable in args:
        iter_list.append(iter(iterable))
    try:
        while True:
            for iterable in iter_list:
                val = next(iter_list[0])
                iter_list.append(iter_list.pop(0))
                yield val
    except StopIteration:
        pass

            
if __name__ == '__main__':
    for i in alternate_all('abcde','fg','hijk'):
        print(i,end='')

我的输出是:

afhbgic

何时应该:

afhbgicjdke

我怎样才能让它忽略耗尽的发电机?我不想使用 itertools 并保持相同的结构。

python generator
3个回答
4
投票

这有效。我试图接近您的原始代码的工作方式(尽管为了简单起见,我确实用列表理解替换了您的第一个循环)。

def alternate_all(*args):
    iter_list = [iter(arg) for arg in args]
    while iter_list:
        i = iter_list.pop(0)
        try:
            val = next(i)
        except StopIteration:
            pass
        else:
            yield val
            iter_list.append(i)

您的代码的主要问题是您的

try
/
except
位于循环之外,这意味着第一个耗尽的迭代器将从循环中退出。相反,您希望在循环内捕获
StopIteration
,这样您就可以继续前进,并且当
iter_list
中仍然有任何迭代器时,循环应该继续前进。


0
投票

直接来自

roundrobin
文档的 itertools-recipe
itertools
怎么样?不过,您最终仍然会使用
itertools.cycle
itertools.islice
,不确定这是否会破坏交易。

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    num_active = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while num_active:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            # Remove the iterator we just exhausted from the cycle.
            num_active -= 1
            nexts = cycle(islice(nexts, num_active))

0
投票

@kaya3的回答基本类似,只是实现形式不同:

def alternate_all(*args):
    iter_list = [iter(arg) for arg in args]

    while iter_list:
        remove_iters = []
        for idx, it in enumerate(iter_list):
            try:
                yield next(it)
            except StopIteration:
                remove_iters.append(idx)
                continue
        for idx in remove_iter:
            iter_list.pop(idx)
© www.soinside.com 2019 - 2024. All rights reserved.