【问题标题】:Multiple yields in a generator expression? Python生成器表达式中的多个产量? Python
【发布时间】:2018-05-02 22:52:41
【问题描述】:

我一直在尝试将此生成器函数转换为生成器表达式,但没有成功。生成器表达式是否可以具有相同的输出?

    def gen5(num):
         for i in range(num):             
             yield i *2
             yield i *10
             yield i *i

    g = gen5(4) 
    list(g)
    [0, 0, 0, 2, 10, 1, 4, 20, 4, 6, 30, 9]

我尝试了以下方法,但得到了这个 (None, None, None) 输出。

    gen_exp2 = (((yield u*2), (yield u*10) ,(yield u*u)) for u in  range(4))

    list(gen_exp2)

    [0,
     0,
     0,
     (None, None, None),
     2,
     10,
     1,
    (None, None, None),
     4,
    20,
     4,
    (None, None, None),
    6,
    30,
    9,
   (None, None, None)]

我也这样做了,但它提供了 4 个嵌套元组,而不是上面的列表。

       gen_exp3 = (((i*2), (i*10), (i*i)) for i in range(4))

       list(gen_exp3)

       [(0, 0, 0), (2, 10, 1), (4, 20, 4), (6, 30, 9)]

另外,如何向生成器表达式添加参数? 非常感谢。

【问题讨论】:

  • 你不需要在生成器表达式中使用yield 语句,用括号括住循环就足够了。
  • 这不会像生成器函数那样生成相同的输出。它给出了 4 个元组。
  • 您将需要另一个嵌套的 for 循环来“解包”您的元组。
  • (x for i in range(num) for x in (i*2, i*10, i*i)) 有效,但读起来令人困惑。 itertools.chain.from_iterable((i*2, i*10, i*i) for i in range(num))
  • 太棒了。谢谢帕特里克。我想了一会儿,我无法将生成器函数转换为 gen 表达式。

标签: python generator yield generator-expression


【解决方案1】:

你只需要在 gen exp 中有一个双循环:

num = 4
g = (j for i in range(num) for j in (i*2, i*10, i*i))
print(*g)

输出

0 0 0 2 10 1 4 20 4 6 30 9

正如 Moses 所说,在 gen exp 中使用 yield 不是一个好主意。


这是一个可爱的变体,使用map

g = (j for i in range(num) for j in map(i.__mul__, (2, 10, i)))

但是,有些人可能不喜欢 i.__mul__ 的这种用法。


您问:“如何向生成器表达式添加参数?”。好吧,你不能。你可以创建一个lambda,正如 Moses 所示,但实际上你最好创建一个适当的生成器函数。 Lambda 应该是匿名函数,因此创建 lambda 并将其绑定到名称是一种相当可疑的做法。 ;)

【讨论】:

  • @JonClements 我想可以。知道它在速度方面如何与i.__mul__ 进行比较会很有趣。
  • 如果它没有因为额外的函数调用和查找而变慢,我会感到惊讶......
  • @JonClements 我也是。 OTOH,我猜partial 可能正在使用一些棘手的 C 魔法来完成它的工作。 functools 的 CPython 源代码有一个 Python 包装器 here。我没有任何运气试图找到 C 源代码。啊,here we go
  • 我的假设是由于 mul 间接寻址,并且它确实需要为参数创建一个 2 元组,所以它的速度会有些慢。不过我还没试过
【解决方案2】:

您不需要在生成器表达式中使用yield

请参阅yield in list comprehensions and generator expressions,了解为什么这很容易被视为错误,尽管事实并非如此。

您可以使用嵌套推导在生成器表达式中实现相同的目的:

gen_exp2 = (x for u in range(4) for x in (u*2, u*10, u*u))

您可以通过将生成器表达式放入函数中来对其进行参数化:

gen_fun = lambda num: (x for u in range(num) for x in (u*2, u*10, u*u)) 

不过,保留原来的方法可能会更好,因为在函数中使用 yield 可能比在生成器表达式中使用嵌套推导更快,而生成器表达式在没有嵌套的情况下已经很慢了。

【讨论】:

  • 理解您的回答。您能否提供问题中显示的输出的解释?只是想知道:)
  • @stack_n_queue 你读过链接的答案吗?几乎解释了解释器如何处理一代中的yield。 exp。以及幕后发生的事情:堆栈等。
猜你喜欢
  • 2011-01-01
  • 1970-01-01
  • 2021-08-26
  • 2014-07-19
  • 2017-12-08
  • 2014-04-02
  • 2021-12-27
  • 2013-05-22
  • 2021-10-17
相关资源
最近更新 更多