Object-Oriented Programming

Object-Oriented Programming#

Class and Object#

Roughly speaking, a class or a type is a blueprint which you use to create objects. An object is an instance of a class.

A class combines data (represented by attributes) and functions (represented by methods) into a single unit.

from math import sqrt

class Vector():
    # custom type for 2d vectors

    # class attribute shared by all objects of this class
    dim = 2
    
    def __init__(self, input_x, input_y):
        # constructor, called when new object is created, Vector(x,y)
        # self is the object being created
        # .x and .y are object attributes
        self.x = input_x
        self.y = input_y
    
    def length(self):
        # length method, returns length of vector
        l = sqrt(self.x**2+self.y**2)
        return l
    
    def scale(self, c):
        # method that scales the vector by a constant c
        # changes the object itself
        # no return value, so returns None
        self.x = self.x * c
        self.y = self.y * c

    def __repr__(self):
        # string representation of the object
        # without this method, it prints the memory address
        return f'({self.x},{self.y})'
            
    
    def add(self, other_vector):
        # method that adds another vector to this vector, returns new vector
        x_new = self.x + other_vector.x
        y_new = self.y + other_vector.y
        return Vector(x_new, y_new)
        
    def __add__(self, other_vector):
        # special method that overloads the + operator
        # without this method, vector + vector would raise an error
        x_new = self.x + other_vector.x
        y_new = self.y + other_vector.y
        return Vector(x_new, y_new)
    
    def normalize(self):
        # method that scales the vector to unit length
        # can call other methods of the same object
        l = self.length()
        self.scale(1/l)
v = Vector(3,4)
v.x
print(v.x)
v.normalize()
print(v.length())
print(v)
3
1.0
(0.6000000000000001,0.8)
v = Vector(1,1)
w = Vector(2,3)

# custom add method
p = v.add(w)
print(p)

# this is actually calling the __add__ method
q = v + w
print(q)
(3,4)
(3,4)

Exercise:

Create a class Matrix2, which represents a 2x2 matrix. The class should have the following attributes:

  • a, b, c, d: Elements of the 2x2 matrix, organized as: $\( \begin{bmatrix} a & b \\ c & d \end{bmatrix} \)$

Methods:

  • A = Matrix2(a, b, c, d): Constructor that initializes the matrix with the given values.

  • determinant(): Calculates the determinant of the matrix.

  • transpose(): Returns a new matrix that is the transpose of the current one.

  • A+B: Adds two matrices together (element-wise addition).

  • print(A) should print the matrix in the following format:

| a  b |
| c  d |
class Matrix:
    def __init__(self, a, b, c, d):
        # constructor to initialize the elements of the matrix
        self.a = a
        self.b = b
        self.c = c
        self.d = d
    
    def determinant(self):
        # calculates the determinant of the matrix
        return self.a * self.d - self.b * self.c
    
    def transpose(self):
        # returns a new matrix that is the transpose of the current one
        return Matrix(self.a, self.c, self.b, self.d)
    
    def __add__(self, other):
        # adds two matrices element-wise
        return Matrix(self.a + other.a, self.b + other.b,
                         self.c + other.c, self.d + other.d)
    
    def __mul__(self, other):
        # performs matrix multiplication
        return Matrix(self.a * other.a + self.b * other.c,
                         self.a * other.b + self.b * other.d,
                         self.c * other.a + self.d * other.c,
                         self.c * other.b + self.d * other.d)
    
    def __repr__(self):
        # string representation of the matrix
        # \n is a newline character
        return f"| {self.a}  {self.b} |\n| {self.c}  {self.d} |"
A = Matrix(1,2,3,4)
print(A)
print(f'det(A) = {A.determinant()}')

B = A.transpose()
print(B)
| 1  2 |
| 3  4 |
det(A) = -2
| 1  3 |
| 2  4 |
C = A + B
print(C)
| 2  5 |
| 5  8 |

Extra, not exam material#

In Python, a Module is a file that contains Python code, such as functions, classes, or variables, which can be reused in other Python programs. For example, math is a built-in module that contains mathematical functions.

We can also create our own module. We can save the definition of the Vector class in a separate file called myVector.py. Then we can reuse the class in other programs by importing the module.

from myVector import Vector
v = Vector(1, 2)

Inheritance: A class can inherit from another class, which means that it gets all the attributes and methods of the parent class. This is useful for code reuse and to create a hierarchy of classes. The child class can override methods of the parent class, or add new methods.

# Parent class
class Animal:
    def __init__(self, name):
        self.name = name

    def sayhello(self):
        print(f"{self.name} says hello")

# Child class inheriting from Animal
class Dog(Animal):
    def speak(self):
        print(f"wof")


# Creating objects of the child classes
dog = Dog("Snoopy")

# It can access the attributes and methods of the parent class
dog.sayhello()

# It also has its own methods
dog.speak()
Snoopy says hello
wof