Python:生成器表达式与产量 [英] Python: generator expression vs. yield

查看:159
本文介绍了Python:生成器表达式与产量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In Python, is there any difference between creating a generator object through a generator expression versus using the yield statement?

使用收益:

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)等

Both functions return generator objects, which produce tuples, e.g. (0,0), (0,1) etc.

一个或另一个的任何优势?有想法吗?

Any advantages of one or the other? Thoughts?

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

Thanks everybody! There is a lot of great information and further references in these answers!

推荐答案

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

There are only slight differences in the two. You can use the dis module to examine this sort of thing for yourself.

编辑:我的第一个版本在交互式提示中反编译了在模块范围内创建的生成器表达式.这与在函数内部使用的OP版本略有不同.我已经对此进行了修改,使其与问题中的实际情况相符.

My first version decompiled the generator expression created at module-scope in the interactive prompt. That's slightly different from the OP's version with it used inside a function. I've modified this to match the actual case in the question.

如下所示,"yield"生成器(第一种情况)在设置中具有三个额外的指令,但与第一个FOR_ITER相比它们仅在一个方面有所不同:"yield"方法使用的是LOAD_FAST代替循环内的LOAD_DEREF. LOAD_DEREF慢得多" LOAD_FAST,因此对于x足够大的值(外部循环),它使"yield"版本比生成器表达式稍微快一点,因为y的值在每次遍历中都稍快地加载.对于较小的x值,由于设置代码的额外开销,它会稍微慢一些.

As you can see below, the "yield" generator (first case) has three extra instructions in the setup, but from the first FOR_ITER they differ in only one respect: the "yield" approach uses a LOAD_FAST in place of a LOAD_DEREF inside the loop. The LOAD_DEREF is "rather slower" than LOAD_FAST, so it makes the "yield" version slightly faster than the generator expression for large enough values of x (the outer loop) because the value of y is loaded slightly faster on each pass. For smaller values of x it would be slightly slower because of the extra overhead of the setup code.

也许值得指出的是,生成器表达式通常会在代码中内联使用,而不是像这样用函数包装它.这样会节省一些设置开销,并且对于较小的循环值,即使LOAD_FAST为"yield"版本提供了优势,也可以使生成器表达式稍快一些.

It might also be worth pointing out that the generator expression would usually be used inline in the code, rather than wrapping it with the function like that. That would remove a bit of the setup overhead and keep the generator expression slightly faster for smaller loop values even if LOAD_FAST gave the "yield" version an advantage otherwise.

在任何情况下,性能差异都不足以证明在一个或另一个之间做出决定是合理的.可读性的重要性要大得多,因此请使用对当前情况最易读的方式.

In neither case would the performance difference be enough to justify deciding between one or the other. Readability counts far more, so use whichever feels most readable for the situation at hand.

>>> 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

这篇关于Python:生成器表达式与产量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆