yield#

yield is a keyword in Python that is used to return from a function while preserving the state of its local variables, and when such a function is called again, execution continues from the yield statement where it was interrupted. Any function containing the yield keyword is called a generator. We can say that yield is what makes it a generator.

Comparison#

Consider an example where we extract only even numbers from a list. basic_fun represents the solution without using yield and yield_fun represents the solution with the yield function.

You can use yield just like return, but remember - it returns to the function for the next iteration.

def basic_fun(list_of_nums):
    return [i for i in list_of_nums if (i%2) == 0]

def yield_fun(list_of_nums):
    for i in list_of_nums:
        if i % 2 == 0:
            yield i

So here both functions have been applied to a list of values. And of course we got the same result:

list_of_nums = [1, 2, 3, 8, 15, 42]
print(
    "basic_fun result:", 
    basic_fun(list_of_nums)
)
print(
    "get_even result:",
    list(yield_fun(list_of_nums))
)
basic_fun result: [2, 8, 42]
get_even result: [2, 8, 42]

Return value#

Functions that exits with yield returns generator - is special object in python.

The following example simply iterates over a list of numbers using the yield operator and prints each element.

The key idea here is that

  • During the function call, the function wasn’t actually executed - we don’t have any output from print, and we got the generator object as a result;

  • But when we unpacked the generator into a list, we got messages and a list as a result.

def experiment_fun(list_of_nums):
    for v in list_of_nums:
        print(f"processing object: {v}")
        yield v

list_of_nums = [10,20,30,40,50]

print("Function execution:")
gen = experiment_fun(list_of_nums)
print("Result:", gen, end = "\n\n")

print("Generator unpacking:")
print("Result:", list(gen))
Function execution:
Result: <generator object experiment_fun at 0x7fada84d4b30>

Generator unpacking:
processing object: 10
processing object: 20
processing object: 30
processing object: 40
processing object: 50
Result: [10, 20, 30, 40, 50]

yield and return#

But what happens if you use both yield and return in the same function? Nothing special - the function will create generator, but if it has return in it - it will just stop iterating over generator.

So the following cell just shows how it might look - it iterates over the passed array while for the first three elements, but then return is executed so that elements after the third have not been processed.

def experiment_fun(list_of_nums):
    for i, v in enumerate(list_of_nums):
        print(f"processing object: {v, i}")
        
        yield v
        
        if i > 1:
            return "test"

list_of_nums = [10,20,30,40,50]
list(experiment_fun(list_of_nums))
processing object: (10, 0)
processing object: (20, 1)
processing object: (30, 2)
[10, 20, 30]