Skip to main content
 首页 » 编程设计

python之itertools.chain() 的广度优先版本

2025年05月04日11del

在 itertools 中有 chain,它将多个生成器组合成一个,本质上对它们进行深度优先迭代,即 chain。 from_iterable(['ABC', '123']) 产生 A、B、C、1、2、3。但是,没有广度优先版本,还是我遗漏了什么?当然有 izip_longest,但是对于大量的生成器来说这感觉很尴尬,因为元组会很长而且可能非常稀疏。

我想到了以下内容:

def chain_bfs(*generators): 
    generators = list(generators) 
    while generators: 
        g = generators.pop(0) 
        try: 
            yield g.next() 
        except StopIteration: 
            pass 
        else: 
            generators.append(g) 

我觉得有点冗长,我是否缺少更 Pythonic 的方法?这个函数是否适合包含在 itertools 中?

请您参考如下方法:

你可以使用 collections.deque()通过你的迭代器旋转;旋转双端队列效率更高。我也将其称为链式 zipper ,而不是“呼吸链”,如下所示:

from collections import deque 
 
def chained_zip(*iterables): 
    iterables = deque(map(iter, iterables)) 
    while iterables: 
        try: 
            yield next(iterables[0]) 
        except StopIteration: 
            iterables.popleft() 
        else: 
            iterables.rotate(-1) 

演示:

>>> list(chained_zip('ABC', '123')) 
['A', '1', 'B', '2', 'C', '3'] 
>>> list(chained_zip('AB', '1234')) 
['A', '1', 'B', '2', '3', '4'] 

还有一个roundrobin() recipe in the documentation做同样的事情,使用 itertools.cycle() function :

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