Reputation: 31339
Is it possible to nest yield from
statements?
The simple form is obvious:
def try_yield1():
x = range(3)
yield from x
Which produces:
0
1
2
But what if I have nested generators?
def try_yield_nested():
x = [range(3) for _ in range(4)]
yield from ((yield from y) for y in x)
This produces:
0
1
2
None # why?
0
1
2
None # ...
0
1
2
None # ...
Why does it produce None
if I used yield from
(even though it is nested)?
I know I can do something like:
from itertools import chain
def try_yield_nested_alternative():
x = [range(3) for _ in range(4)]
yield from chain.from_iterable(x)
Which produces the same output leaving out the None
(which is what I expect). I can also write a simple loop:
for x in [range(3) for _ in range(3)]:
yield from x
But, I thought it would be more pythonic to use nested delegation (preferably even yield from x from y
or yield from x for x in y
, but that is not proper syntax). Why is it not working as I expect?
Upvotes: 5
Views: 287
Reputation: 304
yield
is an expression, it'll evaluate whatever you send(value)
it. Simply put, if you send it nothing, you'll get a None
output because the value of yield
would be None
.
yield from
itself is equal to None
, and you're using two of them. At the second yield from
you're iterating a list, which inputs the generator with the items of that list, but you're also using yield from
to return that generator expression, which returns the list and also itself which is equivalent to None
at each iteration, thus why after each item iteration of range you get a None
.
Essentially this is what is happening:
(yield from y) for y in x
yields the values, [0, 1, 2]yield from
yields the values from the previous, and also yield from
from previous which is None
.None
due to yield from
in (yield from y) for y in x
.Unfortunately, there is no way to get rid of the None
output, due to the nature of the expression. You're better off using from_iterable
.
Source explaining yield expression; https://docs.python.org/2.5/ref/yieldexpr.html
Upvotes: 7