创建一个生成器,它可以从任意数量的内部生成器中生成值。

6
我有一个生成器函数generate,它每次产生5个随机数。我需要以两种方式生成这些数字:
  1. 单次生成,即generate函数的单个输出。
  2. 多次生成,即执行generate多次,并将所有结果作为单个(合并的)流一起产生。
因此,我又编写了另一个函数get_resource,它可以调用generate一次或使用itertools.chain来依次运行多个生成器,但对调用者透明。
我的目标是使用get_resource函数并以相同的格式(一个数字列表)产生结果,无论单次/多次生成。
import itertools
import random


def get_resource(values=None):
    def resource_generator():
        if values:
            # run a generator for every value
            return itertools.chain(generate(value) for value in values)
        else:
            return generate('meh')

    return resource_generator()


def generate(value):
    for _ in range(5):
        yield random.randint(1, 101)


if __name__ == '__main__':
    # list() is used for convenience only, 
    # I still need the values one by one
    print list(get_resource())
    print list(get_resource([1, 2, 3]))

它打印出来的是:
[63, 22, 87, 2, 54]
[<generator object generate at 0x1089f7640>, <generator object generate at 0x1089f7690>, <generator object generate at 0x1089f76e0>]

虽然我需要打印它:

[63, 22, 87, 2, 54]
[63, 22, 87, 2, 54, 1, 58, 79, 89, 77, 94, 99, 30, 30, 4]

我使用Python 2.7版本。


1
为什么要定义 resource_generator() 而不是直接将其代码放在 get_resource() 中? - khelwood
@khelwood,这是我代码的简化和最小版本,周围还有一些其他的事情。在这个例子中它是多余的,但在我的代码中仍然需要它。 - Yury Fedorov
对于2.7版本,您需要使用yield嵌套循环,我已经相应地编辑了我的答案。虽然如果可以的话,我建议您升级到更高版本。 - cs95
2个回答

5
您可以使用 yield from 来指定生成器委派,从 Python 3.3 开始支持。
def get_resource(values=None):
    def resource_generator():
        if values:
            for value in values:
                yield from generate(value)
        else:
            yield from generate(None)

    return resource_generator()

现在,

>>> list(get_resource([1, 2, 3]))
[46, 99, 97, 1, 42, 79, 69, 9, 45, 25, 77, 56, 54, 7, 41]

我尝试了类似于你的Python2代码,但是这种语法在Python3.3之前不被支持:SyntaxError: 'return' with argument inside generator - Yury Fedorov
我只是想理解这个。yield 的存在难道不会自动将此函数变为生成器吗,无论函数路径是否如此?list(get_resource()) 返回 [],这似乎支持了这样一个想法:即 return 触发了 StopIteration - Maarten Fabré
@MaartenFabré 哇,你用的是哪个版本?让我来修复它。 - cs95
Python 3.6;我不是在谈论这个问题如果一个生成器函数在存在from __future__ import generator_stop指令的情况下引发StopIteration,它将被转换为RuntimeError(保留StopIteration作为新异常的原因)。,我只是说生成器中的returnraise StopIteraton <return_value>的语法糖,也可以在这里找到答案。list(get_resource())对你来说返回什么? - Maarten Fabré
@MaartenFabré 你是对的!我的错误在于没有测试我的答案。我已经修正为yield from而不是return。感谢你指出 :) - cs95
显示剩余5条评论

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接