两者之间只有细微的差别。您可以使用该dis
模块自己检查这种事情。
我的第一个版本反编译了在交互式提示符下在module- scope上创建的生成器表达式。这与在函数内部使用的OP版本略有不同。我已对此进行了修改,以匹配问题中的实际情况。
如下所示,“ yield”生成器(第一种情况)在设置中具有三个额外的指令,但与第一个指令相比,FOR_ITER
它们仅在一个方面有所不同:“ yield”方法在循环内部使用LOAD_FAST
代替LOAD_DEREF
。的LOAD_DEREF
是“而较慢的”比LOAD_FAST
,所以它使得“产量”版本略快于对的足够大的值的生成器表达式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