Articles Python Generators and Classes by Thomas Daniel

emailx45

Бывалый
Staff member
Moderator
Python Generators and Classes
Thomas Daniel - 16/Jun/2020
[SHOWTOGROUPS=4,20]

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:
Code:
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:
Code:
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:
Code:
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:
Code:
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:
Code:
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:
Code:
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.
Code:
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.
Code:
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:
Code:
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:
Code:
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:
Code:
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.
Code:
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.

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.

License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

[/SHOWTOGROUPS]
 
Top