Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python generator to yield everything from another generator call

I have a Python generator that can call itself to get more elements to yield. It looks like this:

def gen(list):
    # ...
    if list:
        for x in gen(list[1:]):
            yield x

My question is about the last two lines: is there a more concise way to express this? I am hoping for something like this (understanding this isn't valid Python as-is):

def gen(list):
    # ...
    if list:
        yield each in gen(list[1:])
like image 419
John Zwinck Avatar asked Jun 06 '11 20:06

John Zwinck


People also ask

Is Python yield a generator?

The Yield keyword in Python is similar to a return statement used for returning values or objects in Python. However, there is a slight difference. The yield statement returns a generator object to the one who calls the function which contains yield, instead of simply returning a value.

How do you use for loop yield in Python?

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. Iterating is done using a for loop or simply using the next() function.

Can you iterate over a generator?

One interesting thing to note in the above example is that the value of variable n is remembered between each call. Unlike normal functions, the local variables are not destroyed when the function yields. Furthermore, the generator object can be iterated only once.

What does generator return Python?

Python provides a generator to create your own iterator function. A generator is a special type of function which does not return a single value, instead, it returns an iterator object with a sequence of values.


3 Answers

There's been some call for a yield from or the like that "passes through" all the values returned by a subgenerator. See PEP 380 for some ideas that have been bounced around. However, nothing has been implemented yet. Your first example is correct.

like image 120
kindall Avatar answered Sep 23 '22 03:09

kindall


Python 3.3 added the yield from keyword. Here's a comparison between what you currently have and code using the new keyword:

yield_from_test.py:

def gen_for(a_list):
    if a_list:
        yield a_list[0]
        for x in gen_for(a_list[1:]):
            yield x

def gen_yield(a_list):
    if a_list:
        yield a_list[0]
        yield from gen_yield(a_list[1:])

if __name__ == '__main__':
    assert list(gen_for([1,2,3])) == list(gen_yield([1,2,3]))
    print(list(gen_yield([1,2,3])))

» python3 yield_from_test.py [1, 2, 3]

like image 31
Carl G Avatar answered Sep 24 '22 03:09

Carl G


Your code sample is very idiomatic and concise, no need and no real chance for further improvements and especially not when it comes at readability.

like image 21
Alexander Gessler Avatar answered Sep 24 '22 03:09

Alexander Gessler