Session 10 - Generators

Today you will learn how to make your classes iterable. You will learn how to create a generator function and how to write this in an easier to read manner using a generator expression. You will also gain inside into why a function in python is an object and how to make your own object callable.

We will look at Iterator classes

 1class Compute:
 2    def __iter__(self):
 3            self.last = 0
 4            return self
 5
 6    def __next__(self):
 7            rv = self.last
 8            self.last += 1
 9            if self.last > 10:
10                raise StopIteration()
11            sleep(.5)
12            return rv
13
14 for i in Compute():
15     print(i)

And see how it can be done in an easier to read and use manner with a generator function

1def compute():
2     for i in range(10):
3        yield i

And write a generator expression.

1(i for i in range(10))

Learning goals

  • Understand how functions are abstractions of a class.

  • Create memory and time efficient code using:
    • Iterator Classes

    • generator functions and

    • generator expressoions.

Materials

Exercises

ex1: Python Students

Solution

Based on the Student class below, create a PythonStudents class that acts as a collection of students. The class should implement the iterations protocol (iter(), next() and StopIteration). When iterated the Pythod_students object should return the name of each student in the list.

 1  class PythonStudents:
 2    pass
 3
 4
 5
 6
 7  class Student:
 8
 9     def __init__(self, name, cpr):
10        self.name = name
11        self.cpr = cpr
12
13     @property
14     def name(self):
15             return self.__name
16
17     @name.setter
18     def name(self, name):
19             self.__name = name.capitalize()
20
21     def __add__(self, student):
22             return Student('Anna the daugther', 1234)
23
24     def __str__(self):
25             return f'{self.name}, {self.cpr}'
26
27     def __repr__(self):
28             return f'{self.__dict__}'

ex2: School of students

Solution

In this exercise you start out by having a list of names, and a list of majors.

Your job is to create:

  1. A list of dictionaries of students (ie: students = [{‘id’: 1,’name’: ‘Claus’, ‘major’: ‘Math’}]), cretated in a normal function that returns the result.

  2. A Generator that “returns” a generator object. So the student is yield instead of returned.

Both functions should do the same, but one returns a list and one a generator object.

students = [{‘id’: 1,’name’: ‘Clasu’, ‘major’: ‘Math’}]
The id could be generated by a counter or like in a loop.
The Name should be found by randomly chosing a name from the names list
The Major should be found by randomly chosing a major from the major list
 1names = ['John', 'Corey', 'Adam', 'Steve', 'Rick', 'Thomas']
 2majors = ['Math', 'Engineering', 'CompSci', 'Arts', 'Business']
 3
 4def students_list(num_students):
 5    pass
 6
 7def students_generator(num_students):
 8    pass
 9
10people = students_list(1000000)
11people = students_generator(1000000)

ex3: Range Mimic

Solution

  1. Create a “clone” of the build in range() function, by doing an iterator class.

In the documentation you can read the following about the range function.

class range(start, stop, step=1)
Rather than being a function, range is actually an immutable sequence type, as documented in Ranges and Sequence Types — list, tuple, range.

So the range function is actually not a function, it is a class that implements the iterator protocol.

>>> r = range(1, 10, 2)
>>> i = iter(r)
>>> next(i)
1
  1. Now do the same, but use a generator function instead.