Course: Deep Learning for Solving and Estimating Dynamic Models in Economics and Finance
Script reference: Front matter and Appendix~E — classes, methods, inheritance
Notebook role: primer (pre-course self-study; skip if you write Python every day)
Author: Simon Scheidegger
Python Basics 8: Classes¶
The concept of class allows you to organize your code into objects that have data attributes and functionality.
First, let me just mention that we have already used a class: the list.
Here we are going to learn how to make our own classes.
The syntax for making a class is as follows:
class ClassName(object):
"""
A docstring that explains what the class does.
"""
def __init__(self, arg1, arg2, ...):
"""
A special function that initializes objects of the class.
"""
def other_funcs(self, ...):
"""
A function.
"""I think it is easier to understand what is going on through examples. Let’s make a class representing a 3D vector:
Initially, it won’t do much. But we will give it some functionality as we go.
class Vector(object):
"""
Represents a 3D vector.
"""
def __init__(self, x, y, z):
"""
Initializes the class.
x, y, and z are the i, j, and k cordinates.
"""
self.x = x
self.y = y
self.z = zLet me first show you how the class can be used and what it does.
Then I will try to explain what __init__ does:
r = Vector(0.5, 1.0, 3.0)We have made a simple vector. Notice that we can access the x, y, z components like this:
r.x0.5r.y1.0r.z3.0Data that you can access like this are called class attributes. So, you can inspect the class attributes. You can also change the class attributes like this:
r.x = 2.0r.x2.0Before we add more functionality, notice how the class appears when you ask for help:
help(Vector)Help on class Vector in module __main__:
class Vector(builtins.object)
| Vector(x, y, z)
|
| Represents a 3D vector.
|
| Methods defined here:
|
| __init__(self, x, y, z)
| Initializes the class.
|
| x, y, and z are the i, j, and k cordinates.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
And here is how the class is printed:
print(r)<__main__.Vector object at 0x7f46e845e9b0>
This is not very nice. To print something nicer, you need to add more functionality to the class. We can do this by implementing a special class function called __repr__().
Here is the new code:
class Vector(object):
"""
Represents a 3D vector.
"""
def __init__(self, x, y, z):
"""
Initializes the class.
x, y, and z are the i, j, and k cordinates.
"""
self.x = x
self.y = y
self.z = z
def __repr__(self):
"""
Returns a string representation of the class.
"""
return '{0:1.2f}i + {1:1.2f}j + {2:1.2f}k'.format(self.x, self.y, self.z)Let’s recreate the vector and print it:
r = Vector(0.5, 1.0, 3.0)
print(r)0.50i + 1.00j + 3.00k
Okay, better.
What else would you like to do with vectors? Well, you would probably like to:
multiply a vector with a scalar
add to vectors together
find the dot product of two vectors
find the magnitude of a vector
find the angle between two vectors
find the outer product between two vectors
normalize a vector
find the projection of a vector in a given direction Let’s add all this functionality to the
Vectorgradually so that you start get a feeling of what is going on. Let’s start with scalar multiplication.
class Vector(object):
"""
Represents a 3D vector.
"""
def __init__(self, x, y, z):
"""
Initializes the class.
x, y, and z are the i, j, and k cordinates.
"""
self.x = x
self.y = y
self.z = z
def __repr__(self):
"""
Returns a string representation of the class.
"""
return '{0:1.2f}i + {1:1.2f}j + {2:1.2f}k'.format(self.x, self.y, self.z)
def multiply(self, mu):
"""
Multiply self with the scalar mu.
Returns a new vector.
"""
# Make sure mu is a float
assert isinstance(mu, float)
return Vector(mu * self.x, mu * self.y, mu * self.z)Let’s try it out:
r1 = Vector(0.5, 1.0, 3.0)
r2 = r1.multiply(2.0)
print('r1 = ', r1)
print('r2 = 2 * r1 = ', r2)r1 = 0.50i + 1.00j + 3.00k
r2 = 2 * r1 = 1.00i + 2.00j + 6.00k
Now, let’s explain what is going on inside the class function multiply().
First notice the definition of the function:
def multiply(self, mu):The self argument is a special argument that is required for all class functions. It is a short of handle that an instance of the class object can use to refer to itself.
Inside the body of the function we see the expression:
return Vector(mu * self.x, mu * self.y, mu * self.z)What this does is it creates a new Vector object with x, y, z coordinates given by the x, y, z coordinates of the object calling multiply (which is referred to by self) and it returns the newly created vector.
More specifically, when we do mu * self.x we mean "multiply mu with the x that you can find in the object that is calling multiply, i.e., self.
I understand that this may not be the best possible explanation of these concepts, but we are not going to spend an entire lecture on this. Hopefully, you will pick this up through more examples and throughout the class. But even if you don’t, it’s not vitally important to be able to create your own classes in order to understand the data science content of the course. We will mostly be users of clases. Classes that implement numerical arrays, data frames, plots, data science methods. It is considerably easier to understand how to use clases. If you do want to learn more about classes, however, I suggest that you read this chapter Think Python Chapter 15.
Let’s now add more functionality to the class. I will add a dot product, the magnitude, and the cross product. Study the code below line by line.
import math
class Vector(object):
"""
Represents a 3D vector.
"""
def __init__(self, x, y, z):
"""
Initializes the class.
x, y, and z are the i, j, and k cordinates.
"""
self.x = x
self.y = y
self.z = z
def __repr__(self):
"""
Returns a string representation of the class.
"""
return '{0:1.2f}i + {1:1.2f}j + {2:1.2f}k'.format(self.x, self.y, self.z)
def multiply(self, mu):
"""
Multiply self with the scalar mu.
Returns a new vector.
"""
# Make sure mu is a float
assert isinstance(mu, float)
return Vector(mu * self.x, mu * self.y, mu * self.z)
# The new parts:
def dot(self, r):
"""
Return the dot product of self with r.
"""
return self.x * r.x + self.y * r.y + self.z * r.z
def magnitude(self):
"""
Return the magnitude of self.
"""
# The magnitude is just the square root of the dot
# product of self with self:
return math.sqrt(self.dot(self))
def cross(self, r):
"""
Return the cross product of self with r.
"""
return Vector(self.y * r.z - self.z * r.y,
-(self.x * r.z - self.z * r.x),
self.x * r.y - self.y * r.x)
def normalize(self):
"""
Returns a unit vevtor in the direction of self.
"""
return self.multiply(1.0 / self.magnitude())r1 = Vector(0.5, 1.0, 3.0)
r2 = Vector(2.3, 2.0, -3.0)r1.dot(r2)r3 = r1.cross(r2)
print(r3)r3.dot(r1) # the cross product is orthogonal to the vectors you cross:r1.magnitude()u = r1.normalize()
print(u)u.magnitude()Questions¶
In the code block below, use the function of the class
Vectorto compute the angle betweenr1andr2.
# Your code hereIn the redefinition of the class
Vectorbelow, add a function to that calculates the angle between the calling vector and another vector. Remember that the formula is:
class Vector(object):
"""
Represents a 3D vector.
"""
def __init__(self, x, y, z):
"""
Initializes the class.
x, y, and z are the i, j, and k cordinates.
"""
self.x = x
self.y = y
self.z = z
def __repr__(self):
"""
Returns a string representation of the class.
"""
return '{0:1.2f}i + {1:1.2f}j + {2:1.2f}k'.format(self.x, self.y, self.z)
def multiply(self, mu):
"""
Multiply self with the scalar mu.
Returns a new vector.
"""
# Make sure mu is a float
assert isinstance(mu, float)
return Vector(mu * self.x, mu * self.y, mu * self.z)
# The new parts:
def dot(self, r):
"""
Return the dot product of self with r.
"""
return self.x * r.x + self.y * r.y + self.z * r.z
def magnitude(self):
"""
Return the magnitude of self.
"""
# The magnitude is just the square root of the dot
# product of self with self:
return math.sqrt(self.dot(self))
def cross(self, r):
"""
Return the cross product of self with r.
"""
return Vector(self.y * r.z - self.z * r.y,
-(self.x * r.z - self.z * r.x),
self.x * r.y - self.y * r.x)
def normalize(self):
"""
Returns a unit vevtor in the direction of self.
"""
return self.multiply(1.0 / self.magnitude())
# Your code here:
def angle(self, r):
"""
Return the angle (in radians) between self and r.
"""
# Your code here# Test your code here:
r1 = Vector(0.5, 1.0, 3.0)
r2 = Vector(2.3, 2.0, -3.0)
r1.angle(r2)Now that you wrote your first function with a bit of help, see if you can write one more on your own. Create a function that gives you the projection of a vector in the direction of
self. Say theselfcorresponds to the vector and the other vector is . The formula that gives you the projection is:Call this function
project.
# Copy-paste and add functionality to the Vector class here# Try your code here
r1 = Vector(0.5, 1.0, 3.0)
r2 = Vector(2.3, 2.0, -3.0)
r1.project(r2)