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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Compute:
def __iter__(self):
self.last = 0
return self
def __next__(self):
rv = self.last
self.last += 1
if self.last > 10:
raise StopIteration()
sleep(.5)
return rv
for i in Compute():
print(i)
|
And see how it can be done in an easier to read and use manner with a generator function
1 2 3 | def compute():
for i in range(10):
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
Introduction to Python Generators (excl. Using Advanced Generator Methods & Creating Data Pipelines With Generators)
Exercises
ex1: Python Students
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class PythonStudents:
pass
class Student:
def __init__(self, name, cpr):
self.name = name
self.cpr = cpr
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name.capitalize()
def __add__(self, student):
return Student('Anna the daugther', 1234)
def __str__(self):
return f'{self.name}, {self.cpr}'
def __repr__(self):
return f'{self.__dict__}'
|
ex2: School of students
In this exercise you start out by having a list of names, and a list of majors.
Your job is to create:
A list of dictionaries of students (ie: students = [{‘id’: 1,’name’: ‘Claus’, ‘major’: ‘Math’}]), cretated in a normal function that returns the result.
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.
1 2 3 4 5 6 7 8 9 10 11 | names = ['John', 'Corey', 'Adam', 'Steve', 'Rick', 'Thomas']
majors = ['Math', 'Engineering', 'CompSci', 'Arts', 'Business']
def students_list(num_students):
pass
def students_generator(num_students):
pass
people = students_list(1000000)
people = students_generator(1000000)
|
ex3: Range Mimic
Create a “clone” of the build in range() function, by doing an iterator class. Besides implementing the protocol for beeing iterable, this class should also be callable in order for it to be used like this.
First try this in your interpreter to get inspired:
>>> r = range(1, 10, 2)
>>> next(r)
TypeError: 'range' object is not an iterator
>>> i = iter(r)
>>> next(i)
1
Now do the same, but use a generator function instead.