yield#
The 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.
The followoing cell defines generator that iterates over list but returns only those values that are even. It also prints the corresponding message before returning.
def even_generator(list_of_nums: list[int]):
for i in list_of_nums:
if i % 2 == 0:
print(f"Returning {i}")
yield i
Next code demonstrates the direct result of the generator call.
generator_object = even_generator([1, 2, 4, 7, 10])
generator_object
<generator object even_generator at 0x7e93fc460900>
It’s an object that will produce a result when the function next
is applied to it.
print(next(generator_object), next(generator_object))
Returning 2
Returning 4
2 4
Applying the list
function to it simply collects all the outputs into one list object.
list(even_generator([1, 2, 4, 7, 10]))
Returning 2
Returning 4
Returning 10
[2, 4, 10]
Standard output messages indicate that the next element is computed only when the next
call is applied to the generator. This is actually a really important feature - you can operate on arrays just as you would with regular iterable objects, but they are not necessary in memory, which is imporant when you need to save memory.
Generator object#
Functions that exit using yield
return a generator. A generator is a special kind of iterator that represents a “frozen” procedure, which resumes execution when the program requests the next element.
The following example simply iterates over a list of numbers using the yield
operator and prints each element.
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]
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 thegenerator
object as a result;But when we unpacked the
generator
into a list, we got messages and a list as a result.
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
.
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]