Generators in Python

In the next section, we will discuss how to convert the blocking server that we currently have to a non-blocking one, while keeping the calculating functionalities. In order to do that, we will first need to look into another concept in Python programming, called generators. Chances are you have already worked with Python generators, but to recap, we will go over the key features of generators in this section.

Generators are functions that return iterators and can be paused and resumed dynamically. Return values from generators are often compared to list objects, because generator iterators are lazy (https://en.wikipedia.org/wiki/Lazy_evaluation) and only produce results when explicitly asked. For this reason, generator iterators are more efficient in terms of memory management, and are therefore often preferred over lists when large amounts of data are involved.

Each generator is defined as a function, but instead of using the keyword return inside the function block, we use yield, which is to indicate that the return value is only temporary and the whole generator itself can still be resumed after the return value is obtained. Let's look at how Python generators work in an example, included in the Chapter18/example4.py file, as follows:

# Chapter18/example4.py

def read_data():
for i in range(5):
print('Inside the inner for loop...')
yield i * 2

result = read_data()
for i in range(6):
print('Inside the outer for loop...')
print(next(result))

print('Finished.')

Here, we have a generator named read_data(), which returns multiples of 2, from 0 to 8, in a lazy manner. This is done with the keyword yield, which is placed in front of what would be the return value in an otherwise normal function: i * 2. Note that the yield keyword is placed in front of the individual elements in the iterator that should be sent back, which facilitates the lazy generation.

Now, in our main program, we are obtaining the whole iterator and storing it in the variable result. Then, we loop through that iterator six times, using the next() function (which, evidently, returns the next element in the iterator passed in). After executing the code, your output should be similar to the following:

> python3 example4.py
Inside the outer for loop...
Inside the inner for loop...
0
Inside the outer for loop...
Inside the inner for loop...
2
Inside the outer for loop...
Inside the inner for loop...
4
Inside the outer for loop...
Inside the inner for loop...
6
Inside the outer for loop...
Inside the inner for loop...
8
Inside the outer for loop...
Traceback (most recent call last):
File "example4.py", line 11, in <module>
print(next(result))
StopIteration

You can see that, even though the iterator was generated and returned from the read_data() generator before we looped through it, the actual instructions inside the generator were only executed as we tried to obtain more items from the iterator.

This is illustrated by the fact that the print statements in the output were alternatively placed with each other (one print statement from the outer for loop and one from the inner for loop, alternatively): the execution flow goes into the outer for loop first, tries to access the next item in the iterator, goes into the generator, and goes into its own for loop. As soon as the execution flow reaches the yield keyword, it goes back out to the main program. This process continues until one of the for loops terminates; in our case, the for loop in the generator stopped first, and we therefore encountered a StopIteration error at the end.

The laziness in the generation of the iterator comes from the fact that the generator stops executing when it reaches the yield keyword, and only continues its execution when asked by outside instructions (in this case, by the next() function). Again, this form of data generation is significantly more efficient in memory management than simply generating everything that might need to be iterated over (such as a list).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.97.216