【问题标题】:Is there a way in python to change the order in which yield from get's processed? [duplicate]python中有没有办法改变get的yield处理顺序? [复制]
【发布时间】:2014-11-07 13:27:13
【问题描述】:

如果我这样做:

def foo():
    yield from range(0,10)
    yield from range(10,20)

for num in foo():
    print(num)

我得到一个从 0 到 19 的有序列表。 在不更改范围函数的输入的情况下,有没有一种简单的方法可以指定我想要一个列表:0,10,1,11,2,12...

基本上我首先想要每个生成器的第一个元素。比我想要每个生成器的第二个元素,然后是第三个元素等等。

奖励积分: 有没有办法改变它,以便当生成器产生不等量的结果时,第二个生成器在第一个生成器完成后产生其余的结果?

【问题讨论】:

    标签: python python-3.x generator yield


    【解决方案1】:

    您正在尝试 zip() 您的迭代器;明确地这样做:

    from itertools import chain
    
    def foo():
        yield from chain.from_iterable(zip(range(10), range(10, 20)))
    

    itertools.chain.from_iterable() 的使用让您可以在这里继续使用yield from,将zip() 产生的元组展平。

    演示:

    >>> from itertools import chain
    >>> def foo():
    ...     yield from chain.from_iterable(zip(range(10), range(10, 20)))
    ... 
    >>> list(foo())
    [0, 10, 1, 11, 2, 12, 3, 13, 4, 14, 5, 15, 6, 16, 7, 17, 8, 18, 9, 19]
    

    如果你有不同长度的生成器,你可以使用itertools.zip_longest():

    from itertools import zip_longest
    
    def foo():
        yield from (i for pair in zip_longest(range(10), range(10, 22))
                      for i in pair if i is not None)
    

    我在这里使用了一种不同的扁平化技术,在生成器表达式中使用了双循环。

    这一切确实变得乏味,并且由于您没有将 yield from 与另一个生成器一起使用(因此您不需要支持 generator.send() 和 generator.throw() 来传播),您不妨将其设为正确的循环:

    def foo():
        for x, y in zip_longest(range(10), range(10, 22)):
            if x is not None:
                yield x
            if y is not None:
                yield y            
    

    您还可以使用itertools documentation recipies section 中列出的roundrobin() 配方:

    from itertools import cycle
    
    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))
    
    def foo():
        yield from roundrobin(range(10), range(10, 22))
    

    【讨论】:

    • 如果生成器的长度不同怎么办?这个解决方案似乎默默地忘记了链条的其余部分。我宁愿我仍然得到其余的。
    • @Christian:然后使用itertools.zip_longest,也许会过滤掉None 值。但是您必须明确地这样做。
    • @MartijnPieters 我想我之前已经说过了¹,但是... roundrobin 来自文档,也许? ¹stackoverflow.com/a/23874310/1763356
    • @Veedrac:我总是忘记循环赛!
    猜你喜欢
    • 1970-01-01
    • 2017-06-09
    • 2013-07-04
    • 2021-01-29
    • 1970-01-01
    • 2015-10-15
    • 1970-01-01
    • 2010-09-26
    • 2012-11-11
    相关资源
    最近更新 更多