生成器表达式 vs yield
Generator expressions vs yield
我在看这个视频 (http://pyvideo.org/video/1758/loop-like-a-native-while-for-iterators-genera),最后他谈到了生成器表达式如何与普通生成器方式相同,但事实似乎并非如此和其他主题我读过的是生成器表达式 vs yield 说没有区别。但是,据我所知,每次生成器表达式不使用时,使用 yield 都会返回到 for 循环。它完成它的任务,然后返回到 for 循环。 这在内存使用方面可能会有相当大的差异(取决于您循环的内容),对吧?我的想法对吗?
# generators call yield which will return to the loop it's called in before coming back
def evens(stream):
for n in stream:
if n % 2 == 0:
print("Inside evens")
yield n
# this is the same as above just a generator expression
def evens2(stream):
print("Inside evens2")
return (n for n in stream if n % 2 == 0)
你的想法错了。您的生成器表达式与生成器函数 完全 做同样的事情,只有一个区别:您将 print()
调用放在了错误的位置。在 evens2
中,您在 before 中打印生成器表达式,创建一个生成器对象,而在 evens
中,您在生成器函数本身内部打印。
如果这是 Python 3(或者您使用了 from __future__ import print_function
),您也可以在生成器表达式中使用 print()
函数:
def evens2(stream):
return (print('inside evens2') or n for n in stream if n % 2 == 0)
这相当于:
def evens(stream):
for n in stream:
if n % 2 == 0:
yield print("Inside evens") or n
print()
总是 returns None
,所以 print(..) or n
将 return n
。对任何一个的迭代都将打印并产生所有偶数 n
值。
演示:
>>> def evens2(stream):
... return (print('inside evens2') or n for n in stream if n % 2 == 0)
...
>>> def evens(stream):
... for n in stream:
... if n % 2 == 0:
... yield print("Inside evens") or n
...
>>> g1 = evens([1, 2, 3, 4, 5])
>>> g2 = evens2([1, 2, 3, 4, 5])
>>> g1
<generator object evens at 0x10bbf5938>
>>> g2
<generator object evens2.<locals>.<genexpr> at 0x10bbf5570>
>>> next(g1)
Inside evens
2
>>> next(g2)
inside evens2
2
>>> next(g1)
Inside evens
4
>>> next(g2)
inside evens2
4
这两个调用都会产生一个生成器对象,并且当您使用 next()
将它们向前推进一步时,两个生成器对象都会打印额外的信息。
就Python而言,两个生成器对象生成的字节码大致相同:
>>> import dis
>>> dis.dis(compile('(n for n in stream if n % 2 == 0)', '', 'exec').co_consts[0])
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 27 (to 33)
6 STORE_FAST 1 (n)
9 LOAD_FAST 1 (n)
12 LOAD_CONST 0 (2)
15 BINARY_MODULO
16 LOAD_CONST 1 (0)
19 COMPARE_OP 2 (==)
22 POP_JUMP_IF_FALSE 3
25 LOAD_FAST 1 (n)
28 YIELD_VALUE
29 POP_TOP
30 JUMP_ABSOLUTE 3
>> 33 LOAD_CONST 2 (None)
36 RETURN_VALUE
>>> dis.dis(compile('''\
... def evens(stream):
... for n in stream:
... if n % 2 == 0:
... yield n
... ''', '', 'exec').co_consts[0])
2 0 SETUP_LOOP 35 (to 38)
3 LOAD_FAST 0 (stream)
6 GET_ITER
>> 7 FOR_ITER 27 (to 37)
10 STORE_FAST 1 (n)
3 13 LOAD_FAST 1 (n)
16 LOAD_CONST 1 (2)
19 BINARY_MODULO
20 LOAD_CONST 2 (0)
23 COMPARE_OP 2 (==)
26 POP_JUMP_IF_FALSE 7
4 29 LOAD_FAST 1 (n)
32 YIELD_VALUE
33 POP_TOP
34 JUMP_ABSOLUTE 7
>> 37 POP_BLOCK
>> 38 LOAD_CONST 0 (None)
41 RETURN_VALUE
都用FOR_ITER
循环,COMPARE_OP
看BINARY_MODULO
的输出是否等于0,都用YIELD_VALUE
来yield n
.
的值
我在看这个视频 (http://pyvideo.org/video/1758/loop-like-a-native-while-for-iterators-genera),最后他谈到了生成器表达式如何与普通生成器方式相同,但事实似乎并非如此和其他主题我读过的是生成器表达式 vs yield 说没有区别。但是,据我所知,每次生成器表达式不使用时,使用 yield 都会返回到 for 循环。它完成它的任务,然后返回到 for 循环。 这在内存使用方面可能会有相当大的差异(取决于您循环的内容),对吧?我的想法对吗?
# generators call yield which will return to the loop it's called in before coming back
def evens(stream):
for n in stream:
if n % 2 == 0:
print("Inside evens")
yield n
# this is the same as above just a generator expression
def evens2(stream):
print("Inside evens2")
return (n for n in stream if n % 2 == 0)
你的想法错了。您的生成器表达式与生成器函数 完全 做同样的事情,只有一个区别:您将 print()
调用放在了错误的位置。在 evens2
中,您在 before 中打印生成器表达式,创建一个生成器对象,而在 evens
中,您在生成器函数本身内部打印。
如果这是 Python 3(或者您使用了 from __future__ import print_function
),您也可以在生成器表达式中使用 print()
函数:
def evens2(stream):
return (print('inside evens2') or n for n in stream if n % 2 == 0)
这相当于:
def evens(stream):
for n in stream:
if n % 2 == 0:
yield print("Inside evens") or n
print()
总是 returns None
,所以 print(..) or n
将 return n
。对任何一个的迭代都将打印并产生所有偶数 n
值。
演示:
>>> def evens2(stream):
... return (print('inside evens2') or n for n in stream if n % 2 == 0)
...
>>> def evens(stream):
... for n in stream:
... if n % 2 == 0:
... yield print("Inside evens") or n
...
>>> g1 = evens([1, 2, 3, 4, 5])
>>> g2 = evens2([1, 2, 3, 4, 5])
>>> g1
<generator object evens at 0x10bbf5938>
>>> g2
<generator object evens2.<locals>.<genexpr> at 0x10bbf5570>
>>> next(g1)
Inside evens
2
>>> next(g2)
inside evens2
2
>>> next(g1)
Inside evens
4
>>> next(g2)
inside evens2
4
这两个调用都会产生一个生成器对象,并且当您使用 next()
将它们向前推进一步时,两个生成器对象都会打印额外的信息。
就Python而言,两个生成器对象生成的字节码大致相同:
>>> import dis
>>> dis.dis(compile('(n for n in stream if n % 2 == 0)', '', 'exec').co_consts[0])
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 27 (to 33)
6 STORE_FAST 1 (n)
9 LOAD_FAST 1 (n)
12 LOAD_CONST 0 (2)
15 BINARY_MODULO
16 LOAD_CONST 1 (0)
19 COMPARE_OP 2 (==)
22 POP_JUMP_IF_FALSE 3
25 LOAD_FAST 1 (n)
28 YIELD_VALUE
29 POP_TOP
30 JUMP_ABSOLUTE 3
>> 33 LOAD_CONST 2 (None)
36 RETURN_VALUE
>>> dis.dis(compile('''\
... def evens(stream):
... for n in stream:
... if n % 2 == 0:
... yield n
... ''', '', 'exec').co_consts[0])
2 0 SETUP_LOOP 35 (to 38)
3 LOAD_FAST 0 (stream)
6 GET_ITER
>> 7 FOR_ITER 27 (to 37)
10 STORE_FAST 1 (n)
3 13 LOAD_FAST 1 (n)
16 LOAD_CONST 1 (2)
19 BINARY_MODULO
20 LOAD_CONST 2 (0)
23 COMPARE_OP 2 (==)
26 POP_JUMP_IF_FALSE 7
4 29 LOAD_FAST 1 (n)
32 YIELD_VALUE
33 POP_TOP
34 JUMP_ABSOLUTE 7
>> 37 POP_BLOCK
>> 38 LOAD_CONST 0 (None)
41 RETURN_VALUE
都用FOR_ITER
循环,COMPARE_OP
看BINARY_MODULO
的输出是否等于0,都用YIELD_VALUE
来yield n
.