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>
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)
With b = ((1,), (2,))
, we would expect the output 1, 2
. Indeed, both replicate the output of their respective expression/comprehension forms.
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
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.
- Returning to the question
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.
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
Evaluating list(listfunc())
prints None
, None
(from the append
), and [None, None]
(from the result
) and yields 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:
Don't use yield from
inside of comprehensions. It does not do what you think it does.