列表推导与内部yield
的生成器理解有什么区别?两者都返回一个生成器对象(分别为listcomp
和genexpr
),但在完全评估后,后者会添加看似多余的None
s。
>>> list([(yield from a) for a in zip("abcde", itertools.cycle("12"))])
['a', '1', 'b', '2', 'c', '1', 'd', '2', 'e', '1']
>>> list(((yield from a) for a in zip("abcde", itertools.cycle("12"))))
['a', '1', None, 'b', '2', None, 'c', '1', None, 'd', '2', None, 'e', '1', None]
为什么?什么是科学解释?
答案 0 :(得分:2)
TLDR:生成器表达式使用隐式yield
,它从None
表达式返回yield from
。
实际上两个事情在这里表现不同。你的列表理解实际上已被丢弃......
如果将表达式转换为等效函数,则最容易理解这一点。为清楚起见,让我们写出来:
listcomp = [<expr> for a in b]
def listfunc():
result = []
for a in b:
result.append(<expr>)
return result
gencomp = (<expr> for a in b)
def genfunc():
for a in b:
yield <expr>
要复制初始表达式,关键是将<expr>
替换为(yield from a)
。这是一个简单的文本替换:
def listfunc():
result = []
for a in b:
result.append((yield from a))
return result
def genfunc():
for a in b:
yield (yield from a)
使用b = ((1,), (2,))
,我们会期望输出1, 2
。实际上,两者都复制了各自表达/理解形式的输出。
正如elsewhere所述,yield (yield from a)
应该让您怀疑。但是,result.append((yield from a))
会让你感到畏缩......
让我们先看一下发电机。另一个重写使得显而易见的是:
def genfunc():
for a in b:
result = (yield from a)
yield result
要使此有效,result
必须具有值 - 即None
。生成器不 yield
(yield from a)
表达式,但结果如此。您只获得a
的内容作为评估表达式的副作用。
如果您检查&#34;列表理解&#34;的类型,则不是list
- 它是generator
。 <listcomp>
就是它的名字。是的,那不是月球,它是一个功能齐全的发电机。
还记得我们的转换是如何将yield from
置于函数中的吗? Yepp,这就是你如何定义发电机!
这是我们的功能版本,这次加上print
:
def listfunc():
result = []
for a in b:
result.append((yield from a))
print(result[-1])
print(result)
return result
评估list(listfunc())
打印 None
,None
和[None, None]
以及返回 [1, 2]
。您的实际列表包含潜入发电机的None
!然而,它被扔掉了,结果再次只是副作用。这就是实际发生的事情:
listfunc
时创建生成器。list
迭代它...
yield from a
提供a
的值并返回 None
None
存储在结果列表在迭代结束时......
return
提升StopIteration
,其值为[None, None]
list
构造函数忽略此值并抛出值这个故事的道德
不要在理解中使用yield from
。它没有按照你的想法做到。
答案 1 :(得分:1)
yield from
表达式的值为None
。您的第二个示例是生成器表达式的事实意味着它已经从迭代器隐式地产生,因此它还将产生yield from
表达式的值。有关更详细的答案,请参阅this。
答案 2 :(得分:0)
由于混淆和抛出SyntaxError: 'yield' inside list comprehension
,两个示例在Python 3.8中均已弃用。参见buglog for 3.8 for the release notes。