Python:generator表达式与yield

2024-04-27 02:58:18 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python中,通过生成器表达式创建生成器对象与使用yield语句创建生成器对象有什么区别吗?

使用屈服

def Generator(x, y):
    for i in xrange(x):
        for j in xrange(y):
            yield(i, j)

使用生成器表达式:

def Generator(x, y):
    return ((i, j) for i in xrange(x) for j in xrange(y))

两个函数都返回生成元组的生成器对象,例如(0,0),(0,1)等

有什么好处吗?思想?


谢谢大家!在这些答案中有很多很好的信息和进一步的参考!


Tags: 对象函数答案inforreturn表达式def
3条回答

在这个例子中,不是真的。但是yield可以用于更复杂的构造-for example它还可以接受来自调用方的值,并因此修改流。阅读PEP 342了解更多细节(这是一种值得了解的有趣技术)。

不管怎样,最好的建议是根据自己的需要使用更清晰的内容。

p.S.这里有一个来自Dave Beazley的简单协同程序示例:

def grep(pattern):
    print "Looking for %s" % pattern
    while True:
        line = (yield)
        if pattern in line:
            print line,

# Example use
if __name__ == '__main__':
    g = grep("python")
    g.next()
    g.send("Yeah, but no, but yeah, but no")
    g.send("A series of tubes")
    g.send("python generators rock!")

对于可以放入生成器表达式中的简单循环类型没有区别。然而,yield可以用来创建执行更复杂处理的生成器。下面是生成斐波那契序列的一个简单示例:

>>> def fibgen():
...    a = b = 1
...    while True:
...        yield a
...        a, b = b, a+b

>>> list(itertools.takewhile((lambda x: x<100), fibgen()))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

两者只有细微的差别。您可以使用dis模块自己检查这类事情。

编辑:我的第一个版本在交互提示中反编译了在模块范围内创建的生成器表达式。这与OP的版本略有不同,它在函数中使用。我修改了这个以符合问题中的实际情况。

如下所示,“yield”生成器(第一种情况)在设置中有三个额外的指令,但从第一个FOR_ITER开始,它们只在一个方面有所不同:“yield”方法使用LOAD_FAST代替循环中的LOAD_DEREFLOAD_DEREFLOAD_FAST"rather slower",因此它使“yield”版本比生成器表达式稍快,以获得足够大的x(外循环)值,因为y的值在每次传递时加载得稍快。对于较小的x值,由于设置代码的额外开销,速度会稍微慢一些。

可能还需要指出的是,生成器表达式通常在代码中内联使用,而不是像那样用函数包装它。即使LOAD_FAST为“yield”版本提供了其他优势,这也会减少一些设置开销,并使生成器表达式对于较小的循环值保持稍快的速度。

在这两种情况下,性能差异都不足以证明在两者之间做出决定是合理的。可读性更为重要,所以在手边的情况下,使用感觉最可读的。

>>> def Generator(x, y):
...     for i in xrange(x):
...         for j in xrange(y):
...             yield(i, j)
...
>>> dis.dis(Generator)
  2           0 SETUP_LOOP              54 (to 57)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_FAST                0 (x)
              9 CALL_FUNCTION            1
             12 GET_ITER
        >>   13 FOR_ITER                40 (to 56)
             16 STORE_FAST               2 (i)

  3          19 SETUP_LOOP              31 (to 53)
             22 LOAD_GLOBAL              0 (xrange)
             25 LOAD_FAST                1 (y)
             28 CALL_FUNCTION            1
             31 GET_ITER
        >>   32 FOR_ITER                17 (to 52)
             35 STORE_FAST               3 (j)

  4          38 LOAD_FAST                2 (i)
             41 LOAD_FAST                3 (j)
             44 BUILD_TUPLE              2
             47 YIELD_VALUE
             48 POP_TOP
             49 JUMP_ABSOLUTE           32
        >>   52 POP_BLOCK
        >>   53 JUMP_ABSOLUTE           13
        >>   56 POP_BLOCK
        >>   57 LOAD_CONST               0 (None)
             60 RETURN_VALUE
>>> def Generator_expr(x, y):
...    return ((i, j) for i in xrange(x) for j in xrange(y))
...
>>> dis.dis(Generator_expr.func_code.co_consts[1])
  2           0 SETUP_LOOP              47 (to 50)
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                40 (to 49)
              9 STORE_FAST               1 (i)
             12 SETUP_LOOP              31 (to 46)
             15 LOAD_GLOBAL              0 (xrange)
             18 LOAD_DEREF               0 (y)
             21 CALL_FUNCTION            1
             24 GET_ITER
        >>   25 FOR_ITER                17 (to 45)
             28 STORE_FAST               2 (j)
             31 LOAD_FAST                1 (i)
             34 LOAD_FAST                2 (j)
             37 BUILD_TUPLE              2
             40 YIELD_VALUE
             41 POP_TOP
             42 JUMP_ABSOLUTE           25
        >>   45 POP_BLOCK
        >>   46 JUMP_ABSOLUTE            6
        >>   49 POP_BLOCK
        >>   50 LOAD_CONST               0 (None)
             53 RETURN_VALUE

相关问题 更多 >