Bootcamp
Search…
📅
Course Schedule
0: Language and Tooling Teaching Guide
1: Frontend Basics Teaching Guide
2: Backend Basics Teaching Guide
3: Backend Applications Teaching Guide
4: Backend Structure Teaching Guide
5: Full-Stack Applications Teaching Guide
6: Frontend Infrastructure Teaching Guide
7: React Teaching Guide
8: Advanced React Teaching Guide
9: Advanced Topics Teaching Guide
🧮
Algorithms Teaching Guide
💼
Interview Prep Teaching Guide
☺
User Experience Teaching Guide
A.8: Object-Oriented Programming
oop meme

Introduction

Object-oriented programming (OOP) is the concept that data in our apps can be organised in conceptual entities called "objects" (unrelated to JavaScript Objects), also known as "classes". In an app context, User could be 1 such class, where a user could have multiple attributes such as name, email, password, and also multiple "methods" (another word for functions that are part of a class) that perform functionality on specific user "instances". An "instance" is 1 instantiation of a class, e.g. a User instance that represents Akira. 1 example of a method on the User class could be validatePassword, which might hash an input password and verify if it matches the relevant user's password.
We have already used classes extensively in Coding Bootcamp, perhaps unknowingly. For example, the push and pop methods on arrays in JavaScript are examples of methods in the Array class. The user.name attribute and user.getItems method on a user retrieved with Sequelize are examples of attributes and methods in the Sequelize User class that we define in our model file (with Sequelize helper functions).
In the Leetcode context, we will be using classes to define non-built-in data structures in Python to help us solve problems more efficiently. These new data structures will include linked lists, trees, and graphs. All of these data structures will be built on built-in features of Python.
Object Oriented Objects are different from JavaScript objects. We would generally refer to our usage of JavaScript objects so far as "hash maps". JavaScript objects are confusingly named the same but don't bear any resemblance to Object Oriented objects.

Class and Object Syntax Review

Creating a Class

A class by itself holds no data and does not do anything. It is the abstract representation of data. A class instantiation is an object.
1
class Cat:
2
def __init__(self, name, age, is_male, weight):
3
print("making a new cat")
4
self.name = name
5
6
def get_name(self):
7
# Return the .name attribute of the current instance
8
return self.name
Copied!
Naming convention: Classes are typically named with UpperCamelCase. Instances are typically named with lowerCamelCase. This is the same as we have been doing with Sequelize model classes and instances in Bootcamp.

Objects: Class Instances

Once we have defined a class we can create an instance of the class. An instance is the "live" instantiation of a class, made from the mold of the class definition. It has actual data inside, not just the variable names.
class instance illustration
1
cat1 = Cat("Kai")
2
cat2 = Cat("Chee Kean")
3
​
4
print(cat1.get_name())
5
print(cat2.get_name())
Copied!

Constructor

Notice that when a new Cat is created the __init__ constructor method gets called (and the print function gets called). The constructor is an Object Oriented term that refers to a method that is called when an instance of the class is being created and is used to help set up the values inside of the instance.

self Keyword

When we write code that is for cat1 and cat2 we are going to be calling the instance methods inside the instance. How do we refer to each Cat (i.e., cat1 and cat2) itself? The self keyword is used in Python to refer to the object from within the instance method. So here, self.name refers to the name inside the cat when we call cat1.get_name() - "Kai" in one case and "Chee Kean" in the other- i.e., the name value refers to that instance if we are calling the function for cat1 or cat2.
1
def get_name(self):
2
# we can write a line to return the name for *any* instance
3
return self.name
Copied!

Four Pillars of OOP Design

In Industry there is some disagreement about how to go about doing OOP, or even to do it at all. Four generally recognized principles are: abstraction, encapsulation, inheritance and polymorphism. They were codified at the height of OOP popularity in the 90's by the Three Amigos and the Gang of Four. We'll see some examples for each below. Note that these kinds of programming design terms are not static nor are the universally agreed upon.
Dijkstra quote

Encapsulation: Data & Methods Together

The main usage of objects when writing code is to take advantage of "encapsulating" data into a class instance, and using class methods to manipulate that data.
Without OOP we write code "procedurally" or "functionally"- if the following example were written without OOP, each person's variables and functions would be stored independently of each other, resulting in many variables to keep track of, and more context to pass into each function.

Person Class

1
'''
2
Define a class Person, that initializes (constructor) with 3 args: (name, age, is_male)
3
The object should contain the following attributes
4
1) name (str)
5
2) age (int)
6
3) is_male (bool)
7
4) weight (int)
8
'''
9
class Person:
10
# __init__ is the constructor method and used to initialise the class instance. Parameters passed to __init__ need to be passed in when we initialise the instance, for example Person('akira', '38', True)
11
def __init__(self, name, age, is_male, weight):
12
self.name = name # set the .name attribute to name
13
self.age = age # set the .age attribute to age
14
self.is_male = is_male # set the .is_male attribute to is_male
15
self.weight = weight # set the .weight attribute to weight
16
# By the end of __init__, a new Person object is initialised. No need to return self.
17
18
def get_name(self):
19
# Return the .name attribute of the current instance
20
return self.name
21
22
def get_age(self):
23
return self.age
24
25
def get_weight_pounds(self):
26
return self.weight * 2.2
27
28
def get_gender(self):
29
if self.is_male == True:
30
return "Male"
31
else:
32
return "Female"
Copied!

Run the Code

1
p1 = Person("Koi", 18, True, 60)
2
p2 = Person("Jan", 21, False, 70)
3
​
4
print(p1.name) # "Koi"
5
print(p1.age) # 18
6
print(p1.is_male) # True
7
​
8
print(p1.get_name()) # "Koi"
9
print(p1.get_age()) # 18
10
print(p1.get_gender()) # "Male"
11
​
12
print(p2.get_gender()) # "Female"
Copied!

get_weight_pounds

This class instance method accesses data inside the object, makes a calculation and returns a value. Rather than having a separate helper function for conversion, it is built directly into the code representing the entire person. No arguments have to be passed into the method, because it can refer to the data inside itself.
1
print(p1.get_weight_pounds()) # 132.2
Copied!

Abstraction

Worker Class

1
######################
2
# 2. Worker and Tool #
3
######################
4
​
5
class Worker:
6
def __init__(self, name):
7
self.name = name
8
# Initialise self.tools to empty array for every Worker instance
9
self.tools = []
10
​
11
def pickup(self, tool):
12
''' Put tool in self.tools list if tool does not have owner '''
13
# Do nothing if tool already has owner
14
if not tool.owner:
15
self.tools.append(tool)
16
# Set tool's owner to the current Worker instance
17
tool.owner = self
18
​
19
def show_tools(self):
20
''' Print all tools in self.tools '''
21
result_string = self.name + " has "
22
if not self.tools:
23
result_string += "nothing"
24
else:
25
result_string += str(len(self.tools)) + " tools: "
26
for tool in self.tools:
27
result_string += tool.name + ", "
28
result_string = result_string[:-2] # drop off extra ", "
29
print(result_string)
30
31
def dropall(self):
32
''' Drop all tools in self.tools list '''
33
for tool in self.tools:
34
# Reset tool owner to nobody
35
tool.owner = None
36
self.tools.clear()
Copied!

Tool Class

1
class Tool:
2
def __init__(self, name):
3
# Tool has 2 attributes, name (set on initialisation) and owner (None by default)
4
self.name = name
5
self.owner = None
Copied!

Run the Code

1
# Study the code execution
2
tool1 = Tool("Hammer")
3
tool2 = Tool("Screwdriver")
4
tool3 = Tool("Drill")
5
tool4 = Tool("Hammer")
6
​
7
a = Worker("Aly")
8
b = Worker("Bob")
9
​
10
a.pickup(tool1)
11
a.show_tools() # Prints "Aly has Hammer"
12
a.pickup(tool2)
13
a.pickup(tool3)
14
a.show_tools() # Prints "Aly has 3 tools: Hammer, Screwdriver, Drill"
15
​
16
b.pickup(tool1) # nothing happens because tool1 is owned by Aly
17
b.show_tools() # Prints "Bob has nothing"
18
b.pickup(tool4)
19
b.show_tools() # Prints "Bob has Hammer"
20
​
21
a.dropall()
22
a.show_tools() # Prints "Aly has nothing"
23
b.pickup(tool1)
24
b.show_tools() # Prints "Bob has 2 tools: Hammer, Hammer"
Copied!
When we call the show_tools method, we don't need to worry about how the method code implements the string output. The class is abstracting the string manipulation complexities away.
This is the same whenever we use an NPM library- the library is abstracting away programming complexity we don't need to know about.

Inheritance

Inheritance is a way to refactor the code such that, instead of two classes with similar functionality, we can have one class that gets everything the base class has, but adds on other logic and data.
1
class Robot:
2
3
def __init__(self, name):
4
self.name = name
5
6
def say_hi(self):
7
print("Hi, I am " + self.name)
8
9
class PhysicianRobot(Robot):
10
​
11
def say_hi(self):
12
print("Everything will be okay! ")
13
print(self.name + " takes care of you!")
14
​
15
y = PhysicianRobot("James")
16
y.say_hi()
Copied!

Polymorphism

Polymorphism is inheritance where the same base class can be inherited by multiple classes.
From here.
1
class Bird:
2
def intro(self):
3
print("There are many types of birds.")
4
5
def flight(self):
6
print("Most of the birds can fly but some cannot.")
7
8
class sparrow(Bird):
9
def flight(self):
10
print("Sparrows can fly.")
11
12
class ostrich(Bird):
13
def flight(self):
14
print("Ostriches cannot fly.")
15
16
obj_bird = Bird()
17
obj_spr = sparrow()
18
obj_ost = ostrich()
19
20
obj_bird.intro()
21
obj_bird.flight()
22
23
obj_spr.intro()
24
obj_spr.flight()
25
26
obj_ost.intro()
27
obj_ost.flight()
Copied!

In-Class Exercise

Create a command line battle game.

Starter Code

1
from random import randint
2
​
3
​
4
class Player:
5
def __init__(self):
6
self.hit_points = 10
7
​
8
def take_damage(self, damage):
9
self.hit_points -= damage
10
if self.hit_points <= 0:
11
print("dead!")
12
return False
13
return True
14
​
15
​
16
class Game:
17
def __init__(self):
18
self.opponents = []
19
self.opponent = None
20
self.player = Player()
21
​
22
opponents_count = int(
23
input("how many opponents do you want to fight? "))
24
​
25
for i in range(opponents_count):
26
self.opponents.append(Player())
27
​
28
self.battle()
29
​
30
def battle(self):
31
self.opponent = self.opponents.pop()
32
opponent_dice_roll = randint(1, 6)
33
​
34
while self.opponent.take_damage(opponent_dice_roll):
35
print("he's still alive")
36
​
37
keep_going = input("keep fighting? y/n ")
38
if keep_going == 'y':
39
self.battle()
40
​
41
​
42
game = Game()
Copied!

Player Hits

Add to the game so that the player can also take damage. End the game if they die.

Random Starting Hit Points

Start each opponent with variable hit points.

Monsters

Create a class Monsters that inherits from Player. Monsters can defend an attack a random number of damage between 1 and 3.

Player Weapons

The player begins the game with a sword weapon that does 1:1 damage. Every 3rd turn the player replaces their weapon with a brand new weapon with damage between 1:1 to 1:7.

Player Weapon Armory

Create a list of weapons in the game. Use a dictionary: {"sword":2,"mace":3}.
The player gets to keep several weapons on hand. When they get a chance to get a new weapon, ask if they want to keep it. Throw away the oldest weapon.

Rooms

The player progresses through a series of rooms. The rooms are one after the other. Each room contains a random number of Monsters. Keep an array of rooms in the game.

Player Weapon Selection

Ask the player which weapons they want to keep and which they want to discard. Make inherited classes for each weapon.

Maze

Create a series of rooms. Players can navigate the rooms by turning left and right. Keep the rooms in a 2-D array.

Further Reading

Helpful Videos

  1. 1.
    ​This video introduces key concepts of OOP (encapsulation, abstraction, inheritance, and polymorphism) without getting into much code.
  2. 2.
    ​This video explains the same key concepts of OOP with more code examples.

Four Pillars of OOP

Last modified 2mo ago