This module talks about Python's generator functions for iterators, classes, inheritance, and magic methods.
Introduction
This is the third module in our series on learning Python and its employment in machine learning (ML) and artificial intelligence (AI). In the previous module, we took a look at data structures and loops. Now let's go a bit further with generators and classes.
Generators
One way to create your own iterator is by using a generator function. A generator function uses the yield
keyword to pass the next iterator value to the caller. This is similar to the yield return
keyword in C#. Once the function returns, there's nothing left to iterate over.
Let's demonstrate the yield
keyword using a generator function that yields the first n numbers on the Fibonacci sequence:
def fibonacci(n):
a = 1
b = 1
for i in range(n):
if i < 2:
yield 1
else:
c = a + b
a = b
b = c
yield c
You can now use this function like you use functions such as range
, such as in a loop:
for f in fibonacci(10):
print(f)
This prints the first ten Fibonacci numbers.
You can also use generator functions to yield infinitely many elements.
Classes
Same as C# or Java, Python has classes. Python offers all the standard features of object-oriented programming.
Let's walk through an example of a simple class in Python:
from math import sqrt
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def length(self):
return sqrt(self.x ** 2 + self.y ** 2)
The __init__
method is the constructor.
length
is a class method.
The first argument of a class method refers to the class instance that's being worked on. By convention, this is called self
. (You can name it something else, but no one ever does that.) The role of self
is much like the role of this
in C# and Java, a reference to the current object. The difference in Python is that you cannot use just x
rather than self.x
, and that Python requires you to explicitly include it as the first method argument.
You can now use the class like this:
v = Vector(1, 1)
print(v.length())
print(v.x)
print(v.y)
The x
and y
attributes can be accessed as you see above, but they can be modified as well:
v.x = 2
print(v.length())
Python does not have access modifiers such as public
and private
. All variables are publicly accessible. Starting an attribute name with an underscore serves as a way to tell users of your class that they are not supposed to work with that attribute, but it's not enforced by the language.
Inheritance
Let's demonstrate how you can derive from a class in Python. We'll create a base class Document and a derived class Book:
class Document:
def __init__(self, author, content):
self.author = author
self.content = content
def length(self):
return len(self.content)
def info_summary(self):
return "Document written by " + self.author
class Book(Document):
def __init__(self, author, content, pages):
super().__init__(author, content)
self.pages = pages
def info_summary(self):
return "Book written by {} of {} pages".format(self.author, self.pages)
The Book
class derives from Document
. In the Book
class's __init__
method, this line calls the constructor of the superclass.
super().__init__(author, content)
The info_summary
function is overridden in Book
(nothing like an override
keyword is needed), and there is no mention of length
in Book
so it's just derived from Document
.
book = Book("me", "... content ...", 50)
print(book.length())
print(book.info_summary())
If you want to check whether a certain object is of a certain class, use the isinstance
function:
print(isinstance(book, Book))
print(isinstance(book, Document))
print(isinstance(book, object))
doc = Document("someone else", "...")
print(isinstance(doc, Book))
print(isinstance(doc, Document))
Unlike C# and Java, Python supports multiple inheritance: instead of writing class Book(Document)
, you can write class Book(Document, AnotherClass, PerhapsEvenMore)
.
If the superclasses have methods with the same name, only one of them can be derived in the child class. When a method is called (that's not explicitly overridden), Python uses an algorithm named C3 linearization to determine the order in which to look in the superclasses. If you want to check out the so-called method resolution order, you can look at the YourClassName.__mro__
attribute. Here is an artificial example to demonstrate that:
class A:
pass
class B:
pass
class C:
pass
class D(A, C):
pass
class F(B, C):
pass
class G(A):
pass
class H(F, B, D, A):
pass
print(H.__mro__)
This outputs (<class '__main__.H'>, <class '__main__.F'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>)
so you know that Python will first look in the H class, followed by B, D, A, and finally C.
Magic Methods
Python classes offer a lot of "magic methods," allowing you to do operator overloading, treat your class instance as an iterator, and much more.
A magic method is just like a normal method, but with a specific name in the format __method_name__
. You already know one magic method, __init__
. Another example is the __add__
magic method, to overload the +
operator:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(3, 2)
v2 = Vector(4, 1)
v3 = v1 + v2
The __iter__
and __next__
magic methods enable you to iterate over your instance. This method returns the next iterated value or raises StopIteration
to indicate the end.
class Fibonacci:
def __init__(self, n):
self.prev = 1
self.prev_prev = 1
self.n = n
self.i = 0
def __iter__(self):
return self
def __next__(self):
self.i += 1
if self.i == self.n + 1:
raise StopIteration
if self.i <= 2:
return 1
else:
current = self.prev + self.prev_prev
self.prev_prev = self.prev
self.prev = current
return current
for fib in Fibonacci(10):
print(fib)
This is just the surface of magic methods, there is a lot more you can do. If you're interested, refer to this guide.
Conclusion
In this module, we've talked about generator functions for iterators, classes, inheritance, and magic methods. Now that we've walked through the Python basics, we can take a global look at machine learning-related packages in Python.