How to yield results from a nested generator function?
PythonFunctionNestedGeneratorYieldPython Problem Overview
I have a function which yields results as it downloads them. For the purposes of this question, lets say I yield a sting once every second but I want a convenience function to wrap my generator:
import time
def GeneratorFunction(max_val):
for i in range(0,5):
time.sleep(1)
yield "String %d"%i
def SmallGenerator():
yield GeneratorFunction(3)
for s in SmallGenerator():
print s
...why doesn't that just print the 5 strings I'm expecting? Instead it appears to return the generator functio:
<generator object GeneratorFunction at 0x020649B8>
How can I get this to yield the strings as a normal generator function would?
Python Solutions
Solution 1 - Python
You may have to use the new yield from
, available since Python 3.3, known as “delegated generator”.
If I understood the question correctly, I came to the same issue, and found an answer elsewhere.
I wanted to do something like this:
def f():
def g():
do_something()
yield x
…
yield y
do_some_other_thing()
yield a
…
g() # Was not working.
yield g() # Was not what was expected neither; yielded None.
…
yield b
I now use this instead:
yield from g() # Now it works, it yields x and Y.
I got the answer from this page: Python 3: Using "yield from" in Generators - Part 1 (simeonvisser.com).
Solution 2 - Python
Can't believe I missed this; The answer is to simply return the generator function with suitable arguments applied:
import time
def GeneratorFunction(max_val):
for i in range(0,max_val):
time.sleep(1)
yield "String %d"%i
def SmallGenerator():
return GeneratorFunction(3) # <-- note the use of return instead of yield
for s in SmallGenerator():
print s
Solution 3 - Python
Came here looking for a different form of "nested yield" and finally found the hidden answer. Might not be the best but it works.
I was wanting to yield through a registry tree and here is the solution.
def genKeys(key):
for value in key.values():
yield value
for subkey in key.subkeys():
print(subkey)
for x in genKeys(subkey): #this is the trick
continue
Solution 4 - Python
Here's another small example for generating the multiplication table from 1 to 10:
class Gen1:
def __init__(self, gen2):
self.gen2 = gen2
def __iter__(self):
for a in range(1, 11):
for b in self.gen2:
yield a * b
class Gen2:
def __iter__(self):
for a in range(1, 11):
yield a
def main():
gen2 = Gen2()
gen1 = Gen1(gen2)
for v in gen1:
print(v)
if __name__ == '__main__':
main()
Solution 5 - Python
I came looking for another use of nested yields..
list_of_lists = [ [1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
def iter_all(foo):
yield foo
if isinstance(foo, list):
for i in foo:
for x in iter_all(i):
yield x
print([i for i in iter_all(list_of_lists)])
output:
[[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [1, 2, 3], 1, 2, 3, [4, 5, 6], 4, 5, 6, [7, 8, 9], 7, 8, 9]