我构建了这个稍微神秘的python 3.3:
>>> [(yield from (i, i + 1, i)) for i in range(5)]
<generator object <listcomp> at 0x0000008666D96900>
>>> list(_)
[0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4]
如果我在列表构造函数中使用生成器理解,我会得到不同的结果:
>>> list((yield from (i, i + 1, i)) for i in range(5))
[0, 1, 0, None, 1, 2, 1, None, 2, 3, 2, None, 3, 4, 3, None, 4, 5, 4, None]
为什么列表理解不返回列表?
我可以在python 2中得到类似的奇怪效果(使用集合理解,因为列表推导具有奇数范围):
>>> {(yield i) for i in range(5)}
<generator object <setcomp> at 0x0000000004A06120>
>>> list(_)
[0, 1, 2, 3, 4, {None}]
使用生成器理解时:
>>> list((yield i) for i in range(5))
[0, None, 1, None, 2, None, 3, None, 4, None]
{None}
来自哪里?
答案 0 :(得分:4)
使用this作为参考:
此:
values = [(yield from (i, i + 1, i)) for i in range(5)]
转换为Python 3.x中的以下内容:
def _tmpfunc():
_tmp = []
for x in range(5):
_tmp.append(yield from (i, i + 1, i))
return _tmp
values = _tmpfunc()
这导致values
包含生成器
然后,该生成器将从每个(i, i + 1, i)
得到,直到最终到达return语句。在python 3中,这将throw StopIteration(_tmp)
- 但是,list
构造函数会忽略此异常。
另一方面,这个:
list((yield from (i, i + 1, i)) for i in range(5))
转换为Python 3.x中的以下内容:
def _tmpfunc():
for x in range(5):
yield (yield from (i, i + 1, i))
values = list(_tmpfunc())
这一次,每次yield from
完成时,它都会评估为None
,然后在其他值中yield
。
答案 1 :(得分:3)
List(set,dict)comprehensions转换为与生成器表达式不同的代码结构。让我们看一下集合理解:
def f():
return {i for i in range(10)}
dis.dis(f.__code__.co_consts[1])
2 0 BUILD_SET 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (i)
12 LOAD_FAST 1 (i)
15 SET_ADD 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
与等效的生成器表达式进行比较:
def g():
return (i for i in range(10))
dis.dis(g.__code__.co_consts[1])
2 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 11 (to 17)
6 STORE_FAST 1 (i)
9 LOAD_FAST 1 (i)
12 YIELD_VALUE
13 POP_TOP
14 JUMP_ABSOLUTE 3
>> 17 LOAD_CONST 0 (None)
20 RETURN_VALUE
您会注意到,在生成器表达式具有yield
的位置,set comprehension将一个值直接存储到它正在构建的集合中。
这意味着如果将yield
表达式添加到生成器表达式的主体中,则将其与该语言为生成器主体构造的yield
无法区分对待;因此,每次迭代会得到两个(或更多)值。
但是,如果您将yield
添加到列表(set,dict)理解中,那么理解将从构建列表(set,dict)的函数转换为执行yield
的生成器然后语句返回构造的列表(set,dict)。集合理解结果中的{None}
是根据None
表达式评估的每个yield
构建的集合。
最后,为什么Python 3.3不生成{None}
? (请注意,以前版本的Python 3可以。)这是因为PEP 380(a.k.a。yield from
支持)。在Python 3.3之前,生成器中的return
是SyntaxError: 'return' with argument inside generator
;因此,我们的yield
理解正在利用未定义的行为,但RETURN_VALUE
操作码的实际结果只是从生成器生成另一个(最终)值。在Python 3.3中,明确支持return value
;一个RETURN_VALUE
操作码导致StopIteration
被引发,这样可以停止生成器而不会产生最终值。