15,607,633 members
Articles / Artificial Intelligence / Machine Learning
Article
Posted 16 Jun 2020

14.4K views
6 bookmarked

# Python Generators and Classes

Rate me:
In this article we go a bit further with generators and classes.
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:

Python
```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:

Python
```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:

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:

Python
```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:

Python
```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:

Python
```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.

Python
`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`.

Python
```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:

Python
```print(isinstance(book, Book)) # True
print(isinstance(book, Document)) # True
print(isinstance(book, object)) # True

doc = Document("someone else", "...")
print(isinstance(doc, Book)) # False
print(isinstance(doc, Document)) # True```

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:

Python
```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:

Python
```class Vector:
def __init__(self, x, y):
self.x = x
self.y = y

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.

Python
```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.

Written By
Student
Europe
Also known as ProgramFOX. I like programming, playing chess and astronomy. Administrator of Chess Variants Training[^].

Find me on: