Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional yield or return in python3. How to?

I would like to have a function that can, optionally, return or yield the result. Here is an example.

def f(option=True):
    ...
    for...:
        if option:
            yield result
        else:
            results.append(result)

    if not option:
        return results

Of course, this doesn't work, I have tried with python3 and I always get a generator no matter what option value I set. As far I have understood, python checks the body of the function and if a yield is present, then the result will be a generator. Is there any way to get around this and make a function that can return or yield at will?

like image 665
alec_djinn Avatar asked Nov 17 '15 14:11

alec_djinn


People also ask

How do you return and yield in Python?

The yield statement hauls the function and returns back the value to the function caller and restart from where it is left off. The yield statement can be called multiple times. While the return statement ends the execution of the function and returns the value back to the caller.

How do you use yield instead of return in Python?

Yield is generally used to convert a regular Python function into a generator. Return is generally used for the end of the execution and “returns” the result to the caller statement. It replace the return of a function to suspend its execution without destroying local variables.

Is yield same as return Python?

There are two major differences between the working of yield and return statements in python. Return statement stops the execution of the function. Whereas, yield statement only pauses the execution of the function. The statements written in a program after the return statement are unreachable and are never executed.


2 Answers

You can't. Any use of yield makes the function a generator.

You could wrap your function with one that uses list() to store all values the generator produces in a list object and returns that:

def f_wrapper(option=True):
    gen = f()
    if option:
        return gen    # return the generator unchanged
    return list(gen)  # return all values of the generator as a list

However, generally speaking, this is bad design. Don't have your functions alter behaviour like this; stick to one return type (a generator or an object) and don't have it switch between the two.

Consider splitting this into two functions instead:

def f():
    yield result

def f_as_list():
    return list(f())

and use either f() if you need the generator, and f_as_list() if you want to have a list instead.

Since list(), (and next() to access just one value of a generator) are built-in functions, you rarely need to use a wrapper. Just call those functions directly:

# access elements one by one
gen = f()
one_value = next(gen)

# convert the generator to a list
all_values = list(f())
like image 90
Martijn Pieters Avatar answered Oct 13 '22 09:10

Martijn Pieters


What about this?

def make_f_or_generator(option):
    def f():
        return "I am a function."
    def g():
        yield "I am a generator."
    if option:
        return f
    else:
        return g

This gives you at least the choice to create a function or a generator.

like image 40
elzell Avatar answered Oct 13 '22 10:10

elzell