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?
When you call a function that contains a yield statement anywhere, you get a generator object, but no code runs. Then each time you extract an object from the generator, Python executes code in the function until it comes to a yield statement, then pauses and delivers the object.
yield in Python can be used like the return statement in a function. When done so, the function instead of returning the output, it returns a generator that can be iterated upon. You can then iterate through the generator to extract items.
The keyword yield causes the function to hand back a generator object to its caller. yield will not cause the function to exit nor terminate the loop.
The yield keyword in python works like a return with the only difference is that instead of returning a value, it gives back a generator function to the caller. A generator is a special type of iterator that, once used, will not be available again. The values are not stored in memory and are only available when called.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With