在Python中的迭代器之间交替

时间:2010-01-07 02:46:42

标签: python iterator

在Python中替换从不同迭代器获取值的最有效方法是什么,例如,alternate(xrange(1, 7, 2), xrange(2, 8, 2))将产生1,2,3,4,5,6。我知道一种实现方法它会是:

def alternate(*iters):
    while True:
        for i in iters:
            try:
                yield i.next()
            except StopIteration:
                pass

但是有更高效或更清洁的方式吗? (或者,更好的是,我错过了itertools函数?)

6 个答案:

答案 0 :(得分:16)

对于“干净”实施,您需要

itertools.chain(*itertools.izip(*iters))

但也许你想要

itertools.chain(*itertools.izip_longest(*iters))

答案 1 :(得分:7)

拉链怎么样?你也可以试试itertools的izip

>>> zip(xrange(1, 7, 2),xrange(2, 8 , 2))
[(1, 2), (3, 4), (5, 6)]

如果这不是您想要的,请在您的问题帖子中提供更多示例。

答案 2 :(得分:6)

请参阅roundrobin in the itertools "Recipes" section。这是备用的更通用的版本。

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))

答案 3 :(得分:2)

您可以像这样定义alternate

import itertools
def alternate(*iters):   
    for elt in itertools.chain.from_iterable(
        itertools.izip(*iters)):
        yield elt

print list(alternate(xrange(1, 7, 2), xrange(2, 8, 2)))

这留下了一个问题,即如果一个迭代器在另一个迭代器之前停止该怎么做。 如果您希望继续使用最长的迭代器,那么您可以使用itertools.izip_longest代替itertools.izip

import itertools
def alternate(*iters):   
    for elt in itertools.chain.from_iterable(
        itertools.izip_longest(*iters)):
        yield elt
print list(alternate(xrange(1, 7, 2), xrange(2, 10, 2)))

这将产生收益

[1, 2, 3, 4, 5, 6, None, 8]

当迭代器xrange(1,7,2)引发StopIteration(没有更多元素)时,会产生None

如果你想跳过迭代器而不是屈服None,你可以这样做:

Dummy=object()

def alternate(*iters):   
    for elt in itertools.chain.from_iterable(
        itertools.izip_longest(*iters,fillvalue=Dummy)):
        if elt is not Dummy:
            yield elt

答案 4 :(得分:1)

如果长度相同,则可以像这样使用itertools.izip:

def alternate(*iters):
    for row in itertools.izip(*iters):
       for i in row:
           yield i

答案 5 :(得分:1)

您的尝试有两个问题:

  1. 您不会使用itersiter()中的每个对象换行,因此会因list之类的迭代而失败;和
  2. 通过pass StopIteration您的生成器是一个无限循环。
  3. 一些简单的代码可以解决这两个问题,并且仍然易于阅读和理解:

    def alternate(*iters):
        iters = [iter(i) for i in iters]
        while True:
            for i in iters:
                yield next(i)
    
    >>> list(alternate(range(1, 7, 2), range(2, 8, 2)))
    [1, 2, 3, 4, 5, 6]