In this lecture we will learn classes and the basics of object oriented programming. It's important to know the main idea and be able to write simple classes that serve basic mathematical purposes.
This also prepares us for the scikit-learn
class objects we will use extensively in the second half.
This will explain expressions like x.append(5)
that we used for lists and arr.reshape(4,-1)
for numpy arrays.
import numpy as np
arr = np.arange(10)
# arr. # press tab after the dot
print(arr.reshape(2,-1))
print()
print(np.reshape(arr, [2, -1]))
The main idea of classes is that sometimes you want to put various pieces of data, and functions that manipulate the data in one place. The functions are called methods of the class.
For example, a vector in $\mathbb{R}^3$ is represented by three floats. So why not make a new data type that contains three floats and call it a vector?
Actually, let's make a list of all the things we know we can do to vectors in $\mathbb{R}^3$. We can:
We will make a new type of object in Python, called Vector, that allows us to do all of these things.
import math
# it is always a good habit to import module outside a function or a class
class Vector():
# initialize the vector, we will say
# v = Vector(1,2,3)
# to get a new vector with those coordinated
def __init__(self, xx, yy, zz): # __init__() is initialization
# self is refering to an obj in this class
# xx, yy, zz are the input the user gives
self.x = xx
self.y = yy
self.z = zz
# compute the norm
def norm(self):
return math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
# divide by the norm
def normalize(self):
# this returns the unit vector in the same direction with a Vector obj
# if a function/method has only self as input inside
# the class definition, in executing this function outside
# input can be left empty
no = self.norm()
self.x /= no # self.x = self.x/no
self.y /= no
self.z /= no
# note that every function has self in it, so that it can access the information of the class
The class
specifies what constitutes an object (the variables in it, which are all the variables that appear as self.---
), and the functions that can be used. To create an object of the class, a.k.a. an instance of the class, we need to call the name of the class as if it's a function.
v = Vector(1.0,2.0,3.0)
# the arguments of this call must match the arguments of the __init__
# function in the class, except the self part
u = Vector(2.0, 8.0) # this gives us error b/c zz is missing
type(v)
print(v.x)
print(v.y)
print(v.z)
v.normalize() # normalize function in Vector class is applied on v
print(v.x, v.y, v.z)
print(v)
print
gives us something that tells you that v
is a
vector at some memory location. Whenever we invoke Python's print
command, it first applies the Python repr
function to the item you are printing. The repr
returns a string containing a printable
version of the object you are printing. We can customize this behavior by adding the __repr__(self)
function in this class.
import math
class Vector():
def __init__(self, xx, yy, zz):
self.x = xx
self.y = yy
self.z = zz
def norm(self):
return math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
def normalize(self):
no = self.norm()
self.x /= no
self.y /= no
self.z /= no
def __repr__(self):
# return "<" + str(self.x)[:5] + " , " + str(self.y)[:5] + " , " + str(self.z)[:5] + ">"
return "(" + str(self.x) + " , " + str(self.y) + " , " + str(self.z) + ")"
# note that every function has self in it, so that it can access the information of the class
v = Vector(1.0,2.00000001,3.04383)
print(v) # a little buggy if we just display using 5 chacracters
This should remind of how we used xs.append(5)
for a list xs. Indeed, list
is a class just like our Vector
class, and there is a function called append(self, x)
in it that adds an extra element.
Let's also add dot products, scalar multiplication:
import math
class Vector():
def __init__(self, xx, yy, zz):
self.x = xx
self.y = yy
self.z = zz
def norm(self):
return math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
def normalize(self):
no = self.norm()
self.x /= no
self.y /= no
self.z /= no
def __repr__(self):
return "<" + str(self.x) + " , " + str(self.y) + " , " + str(self.z) + ">"
def dot(self, w):
# dot function's syntax will be v.dot(w)
return self.x * w.x + self.y * w.y + self.z * w.z
# returns a new vector without modifying the original
def times_scalar(self, alpha):
return Vector(alpha * self.x, alpha * self.y, alpha * self.z)
# return (alpha * self.x, alpha * self.y, alpha * self.z)
# this does not return to an obj in the original class
# note that every function has self in it, so that it can access the information of the class
Vector(1.0,2.0,3.0).dot(Vector(1.0,1.0,1.0)) # v.dot(w)
Vector(1.0,2.0,3.0).times_scalar(3)
Vector(1.0, 2.0, 3.0) + Vector(0.0, 5.0, 10.0) # this gives us error...what?
We can also write addition of vectors, we could write add(self,w)
in the class and use v.add(w)
to add v
and w
, but we could also overload the +
operator as follows:
import math
class Vector():
def __init__(self, xx, yy, zz):
self.x = xx
self.y = yy
self.z = zz
def norm(self):
return math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
def normalize(self):
no = self.norm()
self.x /= no
self.y /= no
self.z /= no
def __repr__(self):
return "<" + str(self.x) + " , " + str(self.y) + " , " + str(self.z) + ">"
def dot(self, w):
# every function in a class needs self as the first argument
return self.x * w.x + self.y * w.y + self.z * w.z
def __add__(self, w):
# this function extends the built-in + for Vector class
return Vector(self.x + w.x, self.y + w.y, self.z + w.z)
# note that every function has self in it, so that it can access the information of the class
(Vector(1.0, 2.0, 3.0) + Vector(0.0, 5.0, 10.0))
Vector.dot(Vector(1.0, 2.0, 3.0),Vector(0.0, 5.0, 10.0))
Vector(1.0, 2.0, 3.0).dot(Vector(0.0, 5.0, 10.0)) # same with above
Create a method (function) for Vector
class that computes the cross product of a vector with another.
Create a class called Line
which represents a line with equation $ax + by + c = 0$. Write the __init__(self,a,b,c)
, __repr__(self)
and intersect(self,other_line)
methods for the class. The intersect method should return the coordinates of the intersection point of two lines.