生成器表达式中的多个yield?Python

4

我一直在尝试将这个生成器函数转换为生成器表达式,但是没有成功。有没有可能用生成器表达式得到相同的输出结果?

    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]

我尝试了以下方法,但输出结果为(无,无,无)。
    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)]

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

1
在生成器表达式中,您不需要使用yield语句,将循环用括号括起来就足够了。 - Delgan
它不会生成与生成器函数相同的输出。它提供 4 元组。 - MichaelRSF
你需要另一个嵌套的for循环来“解包”你的元组。 - Delgan
(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)) 会更好一些。 - Patrick Haugh
太棒了。谢谢Patrick。我曾经一度以为我无法将生成器函数转换为生成器表达式。 - MichaelRSF
2个回答

3
您只需要在生成表达式中使用双重循环即可:
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__


你问道:"如何向生成器表达式添加参数?"。实际上你不能这样做。你可以像Moses展示的那样创建一个lambda,但是更好的方式是创建一个正确的生成器函数。Lambdas应该是匿名函数,因此创建一个lambda并将其绑定到名称上是一个相当可疑的做法。 ;)


@JonClements 我想可以这样做。有趣的是要知道它在速度上与 i.__mul__ 相比如何。 - PM 2Ring
如果由于额外的函数调用和查找而变慢,我会感到惊讶... - Jon Clements
@JonClements 我也是。另一方面,我猜partial可能使用了一些巧妙的C语言技巧来完成它的工作。CPython中functools的源代码有一个Python包装器在这里。我试图找到C源代码,但没有什么好运。啊,我们找到了 - PM 2Ring
我的假设是,partial 函数可能会稍微慢一些,因为它需要进行 mul 间接操作,并且需要为参数创建一个 2 元组。不过我还没有尝试过。 - Antti Haapala -- Слава Україні

2

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

请参考列表推导式和生成器表达式中的yield,了解为什么这可能被认为是一个bug,尽管它并不是。

您可以使用嵌套推导式来在生成器表达式中实现同样的功能:

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可能比在生成器表达式中使用嵌套推导要快,而嵌套推导本身已经很慢了。


明白了你的回答。你能否提供一下问题中输出结果的解释呢?只是好奇想知道 :) - Abhijith Asokan
@stack_n_queue 你读了链接的答案吗?它非常清楚地解释了解释器如何处理生成表达式中的 yield,以及幕后发生的事情:堆栈等。 - Moses Koledoye

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