Python 遍历一系列生成器

Python 遍历一系列生成器,python,algorithm,Python,Algorithm,我有一个生成器序列:(genu_0,genu_1,…gen_n) 这些生成器将惰性地创建它们的值,但它们是有限的,并且可能具有不同的长度 我需要能够构造另一个生成器,按顺序生成每个生成器的第一个元素,然后是第二个元素,依此类推,跳过已耗尽的生成器中的值 我认为这个问题类似于取元组 ((1, 4, 7, 10, 13, 16), (2, 5, 8, 11, 14), (3, 6, 9, 12, 15, 17, 18)) 并遍历它,这样它将按顺序产生从1到18的数字 我正在使用(genA,genB

我有一个生成器序列:(genu_0,genu_1,…gen_n)

这些生成器将惰性地创建它们的值,但它们是有限的,并且可能具有不同的长度

我需要能够构造另一个生成器,按顺序生成每个生成器的第一个元素,然后是第二个元素,依此类推,跳过已耗尽的生成器中的值

我认为这个问题类似于取元组

((1, 4, 7, 10, 13, 16), (2, 5, 8, 11, 14), (3, 6, 9, 12, 15, 17, 18))
并遍历它,这样它将按顺序产生从1到18的数字

我正在使用(genA,genB,genC)解决这个简单的例子,genA的产量值来自(1,4,7,10,13,16),genB产量(2,5,8,11,14)和genC产量(3,6,9,12,15,17,18)

要解决元组中的元组的更简单问题,答案相当简单,如果 元组的元素长度相同。如果变量“a”引用元组,则可以使用

[i for t in zip(*a) for i in t]
不幸的是,项目不一定是相同的长度和拉链技巧似乎并不适用于发电机无论如何

到目前为止,我的代码非常难看,我没有找到一个干净的解决方案。帮助?

我想你需要帮助


您可以考虑<代码> ItRealths.IZIPixLime,但如果没有一个有效值,则该解决方案将失败。这是一个“另一台发电机”示例,它完全符合您的要求,非常干净:

def my_gen(generators):
    while True:
        rez = () 
        for gen in generators:
            try:
                rez = rez + (gen.next(),)
            except StopIteration:
                pass
        if rez:
            yield rez
        else:
            break

print [x for x in my_gen((iter(xrange(2)), iter(xrange(3)), iter(xrange(1))))]

[(0, 0, 0), (1, 1), (2,)] #output

另一个itertools选项,如果您希望它们全部折叠在一个列表中;这(正如@gg.kaspersky已经在另一个线程中指出的)不会处理生成的
None

g = (generator1, generator2, generator3)

res = [e for e in itertools.chain(*itertools.izip_longest(*g)) if e is not None]
print res

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

如果您查看的文档,您将看到它提供了一个纯Python实现。修改此实现很容易,因此它可以生成您需要的结果(即,就像
izip_longest
,但没有任何
fillvalue
):


itertools.izip_longest
;你可以通过一个哨兵来填补发电机的不足。如果您愿意,您可以从结果中过滤出该哨兵。我建议使用“If e not None”。此外,如果None是有效值,该怎么办?很容易处理
None
的情况并保持这种方法。只需事先添加一行
sentinel=object()
,然后使用
e is not sentinel
fillvalue=sentinel
[啊,我刚刚注意到@katriealex在这个建议上比我领先了好几个小时。我会留下评论,但承认它太晚了。]你可以使用
iter(范围(x))
而不是
simple\u-gen(x)
。或者更好的是:
iter(xrange(x))
在Python 2.x上。谢谢所有的答案。最后一个完全符合我的要求,我能理解。但就我正在编写的代码而言,出于性能原因,将使用基于itertools.izip_最长的解决方案。另一个案例是我阅读文档,但没有看到可能的用途。。。
g = (generator1, generator2, generator3)

res = [e for e in itertools.chain(*itertools.izip_longest(*g)) if e is not None]
print res

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
class ZipExhausted(Exception):
    pass

def izip_longest_nofill(*args):
    """
    Return a generator whose .next() method returns a tuple where the
    i-th element comes from the i-th iterable argument that has not
    yet been exhausted. The .next() method continues until all
    iterables in the argument sequence have been exhausted and then it
    raises StopIteration.

    >>> list(izip_longest_nofill(*[xrange(i,2*i) for i in 2,3,5]))
    [(2, 3, 5), (3, 4, 6), (5, 7), (8,), (9,)]
    """
    iterators = map(iter, args)
    def zip_next():
        i = 0
        while i < len(iterators):
            try:
                yield next(iterators[i])
                i += 1
            except StopIteration:
                del iterators[i]
        if i == 0:
            raise ZipExhausted
    try:
        while iterators:
            yield tuple(zip_next())
    except ZipExhausted:
        pass
def iter_round_robin(*args):
    """
    Return a generator whose .next() method cycles round the iterable
    arguments in turn (ignoring ones that have been exhausted). The
    .next() method continues until all iterables in the argument
    sequence have been exhausted and then it raises StopIteration.

    >>> list(iter_round_robin(*[xrange(i) for i in 2,3,5]))
    [0, 0, 0, 1, 1, 1, 2, 2, 3, 4]
    """
    iterators = map(iter, args)
    while iterators:
        i = 0
        while i < len(iterators):
            try:
                yield next(iterators[i])
                i += 1
            except StopIteration:
                del iterators[i]