内含`yield`的列表理解和生成器理解之间的区别 [英] Difference between list comprehension and generator comprehension with `yield` inside
问题描述
内部包含yield
的列表推导和生成器推导之间有什么区别?两者都返回一个生成器对象(分别为listcomp
和genexpr
),但是在充分评估后,后者会添加似乎相当多余的None
s.
What is the difference between list comprehensions and generator comprehensions with yield
inside? Both return a generator object (listcomp
and genexpr
respectively), but upon full evaluation the latter adds what seem to be rather superfluous 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]
为什么?科学解释是什么?
How come? What is the scientific explanation?
推荐答案
TLDR:生成器表达式使用隐式yield
,该隐式yield
从yield from
表达式返回None
.
TLDR: A generator expression uses an implicit yield
, which returns None
from the yield from
expression.
这里实际上有两行为不同.您的列表理解实际上已被丢弃...
There are actually two things behaving differently here. Your list comprehension is actually thrown away...
- 再次清楚
如果将表达式转换为等效函数,则最容易理解.为了清楚起见,让我们将其写出来:
Understanding this is easiest if you transform the expressions to equivalent functions. For clarity, let's write this out:
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)
.这是一个简单的文本替换:
To replicate the initial expressions, the key is to replace <expr>
with (yield from a)
. This is a simple textual replacement:
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
.确实,两者都复制了各自表达/理解形式的输出.
With b = ((1,), (2,))
, we would expect the output 1, 2
. Indeed, both replicate the output of their respective expression/comprehension forms.
如在其他地方所述,yield (yield from a)
应该使您感到怀疑.但是,result.append((yield from a))
应该会让您感到畏缩...
As explained elsewhere, yield (yield from a)
should make you suspicious. However, result.append((yield from a))
should make you cringe...
- 提供答案
让我们先看一下生成器.另一次重写使显而易见的事情发生了:
Let's look at the generator first. Another rewrite makes it obvious what is going on:
def genfunc():
for a in b:
result = (yield from a)
yield result
要使其有效,result
必须具有一个值-即None
.生成器不会不 yield
(yield from a)
表达式,但是会生成结果.您只能得到a
的内容,这是评估表达式的副作用.
For this to be valid, result
must have a value - namely None
. The generator does not yield
the (yield from a)
expression, but its result. You only get the content of a
as a side effect of evaluating the expression.
- 返回问题
如果您检查列表理解"的类型,则不是list
-是generator
. <listcomp>
只是它的名字.是的,那不是月亮,而是功能齐全的发生器.
If you check the type of your "list comprehension", it is not list
- it is generator
. <listcomp>
is just its name. Yes, that's not a Moon, that's a fully functional generator.
还记得我们的转换如何将yield from
放入函数中吗?是的,这就是您定义生成器的方式!
这是我们的函数版本,这次是在上面撒上print
:
Remember how our transformation put a yield from
inside a function? Yepp, that is how you define a generator!
Here is our function version, this time with print
sprinkled on it:
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
!但是,它被扔掉了,结果再次只是副作用.这是实际发生的情况:
Evaluating list(listfunc())
prints None
, None
, and [None, None]
and returns [1, 2]
. Your actual list contains those None
that sneaked into the generator as well! However, it is thrown away and the result is again just a side effect. This is what actually happens:
- 通过评估列表理解力/
listfunc
,将创建一个生成器. - 将其馈送到
list
对其进行迭代...-
yield from a
提供a
的值并返回None
-
None
存储在结果列表中
- A generator is created upon evaluating the list comprehension/
listfunc
. - Feeding it to
list
iterates over it...yield from a
provides the values ofa
and returnsNone
None
is stored in the result list
在迭代结束时...
-
return
引发StopIteration
且值为[None, None]
-
list
构造函数将忽略此值并将其丢弃
return
raisesStopIteration
with a value of[None, None]
- The
list
constructor ignores this and throws the value away
这个故事的寓意
请勿在理解内使用
yield from
.它没有按照您的想象做.Don't use
yield from
inside of comprehensions. It does not do what you think it does.这篇关于内含`yield`的列表理解和生成器理解之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
-