Chapter 7: Object Oriented Programming
Chapter 7: Object Oriented Programming
Procedural Programming: writing programs made of functions that perform similar tasks
- Procedures typically operate on data items that are separate from the procedures
- Data items commonly passed from one procedure to another

- Focus: to create procedures that operate on program’s data
Object-Oriented Programming: focused on creating objects
- Objects = entity that contains data and procedures
- Data attributes = data
- Methods = procedures that perform operations on the data attributes
Encapsulation: combining data (attributes) and procedures (methods) into a single object
- OOP allows for ~, single object = single class in OOP
- Ensures that the private properties (attributes) can only be accessed / altered (modified) using public methods
- VS procedural programming where attributes and methods are all separate entities
- Main purpose: data integrity (protects integrity of the data)
- Restricts direct access to attributes of an object, so users cannot access state values for all of the variables of a particular object
Data hiding: object’s data attributes are hidden from code outside the object and access is restricted to the object’s methods
- Protects from accidental corruption
- Outside code does not need to know internal structure of the object
Object
- Data attributes: define the state of an object
- E.g. clock object has
second,minute, andhourdata attributes - Public methods: allow external code to manipulate object
- E.g.
set_time,set_alarm_time - Private methods: used for object’s inner workings
Class vs Instance/Object
- Class: code that specifies the data attributes and method of a particular type of object
- Similar to blueprint of a house or a cookie cutter
- Instance: an object created from a class
- Similar to a specific house build according to the blueprint, or a specific cookie
- There can be many instances of one class
- E.g. housefly and mosquito objects are instances of the Insect class

Class definitions
- Set of statements that define a class’s methods and data attributes
- Begin with
class Class_names: - Class names often start with uppercase letter
- Method definition like any other python function definition
selfparameter: required in every method in the class
⇒ references the specific object that the method is working on- Initializer method (i.e. class’s constructor)
- Automatically executed when an instance of the class is created
- Initializes object’s data attributes and assigns
selfparameter to object def __init__(self):- Usually the first method in a class definition
import random
# The Coin class simulates a coin that can be flipped.
class Coin:
# initializes the sideup data attribute with 'Heads'.
def __init__(self):
self.sideup = 'Heads'
# The toss method generates a random number in the range of 0 to 1.
# If the number is 0, then sideup is set to 'Heads'. Otherwise, sideup is set to 'Tails'
def toss(self):
if random.randint(0,1) == 0:
self.sideup = 'Heads'
else: self.sideup = 'Tails'
# The get_sideup method returns the value referenced by sideup
def get_sideup(self):
return self.sideup- Creating an Instance of a class
- Call the initializer method
My_instance = Class_Name() - Use dot notation to call any of the class methods using the created instance
My_instance.method() - Because the
selfparameter references the specific instance of the object, the method will affect this instance, reference toselfis passed automatically
# The main function.
def main():
# Create an object from the Coin class
my_coin = Coin()
# Display the side of the coin that is facing up
print('This side is up:', my_coin.get_sideup())
for count in range(10):
my_coin.toss()
print(my_coin.get_sideup())
# Call the main function.
main()Actions caused by the Coin() expression

The my_coin variable references a Coin object

Data Hiding
- Hiding Attributes
- An object’s data attributes should be private
- To denote private attribute or method: name prefixed with an underscore (e.g. _sideup)
- Accessor and Mutator Methods
- All of a class’s data attributes are private and provide methods to access and change them
- Accessor / Getter methods: return a value from a class’s attribute without changing it ⇒ safe way for code outside the class to retrieve value of attributes
- Mutator / Setter methods: store or change value of a data attribute
- Storing classes in modules
- Filename for module must end in .py
- Module can be imported to programs that use the class
Class methods can have multiple parameters in addition to self
- For
__init__, parameters needed to create an instance of the class - E.g. a
BankAccountobject is created with a balance - When called, the initializer method receives a value to be assigned to the _balance attribute
- For other methods, parameters needed to perform required task
- E.g.
depositmethod amount to be deposited
E.g. the BankAccount Class
account.py
# The BankAccount class simulates a bank account
class BankAccount:
# The __init__ method accepts an argument for the account’s balance.
# It is assigned to the _balance attribute
def __init__(self, bal):
self._balance = bal
# The deposit method makes a deposit into the account
def deposit(self, amount):
self._balance += amount
# The withdraw method withdraws an amount from the account
def withdraw(self, amount):
if self._balance >= amount:
self._balance -= amount
else:
print(‘Error: insufficient funds’)
return
# The get_balance method returns the account balance
def get_balance(self):
return self._balanceaccount_test.py
# This program demonstrates the BankAccount class
import account
def main():
# Get starting balance
start_bal = float(input(‘Enter your starting balance: ’))
# Create a BankAccount object
savings = account.BankAccount(start_bal)
# Deposit user’s paycheck
pay = float(input(‘How much were you paid this week?’))
print(“I will deposit that into your account.”)
savings.deposit(pay)
# Display balance
print(f‘Your account balance is ${savings.get_balance():.2f}’)
# Get amount to withdraw
cash = int(input(‘How much would you like to withdraw?’)
print(‘I will withdraw that from your account’)
savings.withdraw(cash)
# Display balance
print(f‘Your account balance is ${savings.get_balance():.2f’)
# Call main function
main()
The __str__ method
- Object’s state = value of object’s attribute at a given moment
- Displays object’s state
- Automatically called when you pass the object’s name to the print statement
- Automatically called when the object is passed as an argument to the str function
# the __str__ method returns a string indicating the object’s state
def __str__(self):
state_string = f‘Your account balance is ${self._balance:.2f}.’
return state_string
# display balance
print(savings)Working with instances
- Instance attribute: belongs to a specific instance of a class
- Created when a method uses a
selfparameter to create an attribute - If many instances of a class are created, each would have its own set of attributes
simulation.py
import random
# The Coin class simulates a coin that can be flipped
class Coin:
# The __init__ method initializes the _sideup data attribute with ‘Heads’.
def __init__(self):
self._sideup = ‘Heads’
# The toss method generates a random number in the range of 0 to 1
# If the number is 0, then sideup is set to ‘Heads’. Otherwise, sideup is set to ‘Tails’
def toss(self):
if random.randint(0,1) == 0:
self._sideup = ‘Heads’
else:
self._sideup = ‘Tails’
# The get_sideup method returns the value referenced by sideup
def get_sideup(self):
return self._sideupcoin_demo.py
# This program imports the simulation module and creates 3 instances of the Coin class
import simulation
def main():
# Create 3 objects from the Coin class
coin1 = simulation.Coin()
coin2 = simulation.Coin()
coin3 = simulation.Coin()
# Display the side of each coin that is facing up
print(‘I have 3 coins with these sides up: ’)
print(coin1.get_sideup())
print(coin2.get_sideup())
print(coin3.get_sideup())
print()
# Toss the coins
print(‘I am tossing all 3 coins…’)
print()
coin1.toss()
coin2.toss()
coin3.toss()
# Display the side of each coin that is facing up
print(‘Now here are the sides that are up: ’)
print(coin1.get_sideup())
print(coin2.get_sideup())
print(coin3.get_sideup())
print()
# Call the main function
main()The coin1, coin2, and coin3 variables reference 3 Coin objects

The objects after the toss method

Passing objects as arguments
- Methods and functions often need to accept objects as arguments
- When you pass an object as an argument, you are actually passing a reference to the object. The receiving method or function has access to the actual object
- Methods of the object can be called within the receiving function or method, and data attributes may be changed using mutator methods
Techniques for designing classes
- Class diagram is a standard diagram for graphically depicting object-oriented systems
- General layout: box divided into 3 sections:
- Top section: name of class
- Middle section: list of data attributes
- Bottom section: list of class methods
- Access Modifier:
-
- private
-
- public
- E.g.
| Class diagram for the Coin class | Class diagram for the Customer Class Coin | Customer -sideup | -name +toss() | -address +get_sideup() | -phone +set_name() +set_address() +set_phone() +get_name() +get_address() +get_phone() |
|---|
Defining a simple class
- Think about the behaviour and attributes of objects of new class
- Choose an appropriate class name and develop a short list of the methods available to users
- Write a short script that appears to use the new class in an appropriate way
- Choose appropriate data structures for attributes
- Fill in class template with __init__ and __str__
- Complete and test remaining methods incrementally
- Document your code
Inheritance
- In reality, many objects are a specialised version of more general objects
- E.g. grasshoppers and bees are specialized types of insects, have unique characteristics
- “Is a” relationship: exists when one object is a specialized version of another object
- Specialized object has all the characteristics of the general object + unique characteristics
- E.g. rectangle is a shape, car is a vehicle
- Inheritance**:** used to create an “is a” relationship between classes
- Attributes & methods from the superclass are used as a basis for the subclass
- Superclass (base class): a general class
- Subclass (derived class): a specialized class, an extended version of the superclass
- Inherits attributes and methods of the superclass
- New attributes and methods can be added
E.g. need to create classes for cars, pickup trucks, and SUVs
All are automobiles
Have a make, year model, mileage, and price
This can be the attributes for the base case
In addition:
Car has a number of doors
Pickup truck has a drive type
SUV has a passenger capacity
In a class definition for a subclass:
To indicate inheritance, the superclass name is placed in parentheses after subclass name, eg. class Car(Automobile):
The initializer method of a subclass calls the initializer method of the superclass and then initializes the unique data attributes
Add method definitions for unique methods
vehicles.py
# The Automobile class holds general data about an automobile
class Automobile:
# The __init__ method accepts arguments for the make, model, mileage, and price. It initializes the data attributes with these values
def __init__(self, make, model, mileage, price):
self._make = make
self._model = model
self._mileage = mileage
self._price = price
# The following methods are mutators for the class’s data attributes
def set_make(self, make):
self._make = make
def set._model(self, model):
self._model = model
def set._mileage(self, mileage):
self._mileage = mileage
def set_price(self, price):
self._price = price
# The following methods are the accessors for the class’s data attributes
def get_make(self):
return self._make
def get_model(self):
return self._model
def get_mileage(self):
return self._mileage
def get_price(self):
return self._price
# The Car class represents a car. It is a subclass of the Automobile class.
class Car(Automobile):
# The __init__ method accepts arguments for the car’s make, model, mileage, price, and doors
# Note: init method is only required if there is any change to the attribute settings
def __init__(self, make, model, mileage, price, doors):
# call the superclass’s __init__ method & pass the required arguments (i.e. inherit attributes from superclass)
super().__init__(make, model, mileage, price)
# initialise the __doors attribute (new attribute)
self._doors = doors
# the set_doors method is the mutator for the __doors attribute
def set_doors(self, doors):
self._doors = doors
# the get_donors method is the accessor for the __doors attribute
def get_doors(self):
return self._doors
# The Truck class represents a pickup truck. It is the subclass of the Automobile class
class Truck(Automobile):
# The __init__ method accepts arguments for the car’s make, model, mileage, price, and drive type
def __init__(self, make, model, mileage, price, drive_type):
# Call the superclass’s __init__ method and pass the required arguments
super().__init__(make, model, mileage, price)
# Initialize the _drive_type attribute
self._drive_type = drive_type
# The set_drive_type method is the mutator for the _drive_type attribute
def set_drive_type(self, drive_type):
self._drive_type = drive_type
# The get_drive_type method is the accessor for the _drive_type attribute
def get_drive_type(self):
return self._drive_type
# The SUV class represents a sports utility vehicle. It is a subclass of the Automobile class
class SUV(Automobile):
# The __init__ method accepts arguments for the car’s make, model, mileage, price, and passenger capacity
def __init__(self, make, model, mileage, price, pass_cap):
# Call the superclass’s __init__ method and pass the required arguments
super().__init__(make, model, mileage, price)
# Initialize the _pass_cap attribute
self._pass_cap = pass_cap
# The set_pass_cap method is the mutator for the _pass_cap attribute
def set_pass_cap(self, pass_cap):
self._pass_cap = pass_cap
# The get_pass_cap method is the accessor for the _pass_cap attribute
def get_pass_cap(self):
return self._pass_capcar_truck_suv_demo.py
# This program creates a Car object, a Truck object, and an SUV object
import vehicles
def main():
# Create a Car object for a used 2001 BMW with 70,000 miles, priced at $15,000, with 4 doors (create subclass object)
car = vehicles.Car(‘BMW’, 2001, 70000, 15000.0, 4)
# Create a Truck object for a used 2002 Toyota pickup with 40,000 miles, priced at $12,000, with 4-wheel drive
truck = vehicles.Truck(‘Toyota’, 2002, 40000, 12000.0, ‘4WD’)
# Create an SUV object for a used 2000 Volvo with 30,000 miles, priced at $18,500, with 5 passenger capacity
suv = vehicles.SUV(‘Volvo’, 2000, 30000, 18500.0, 5)
print(‘USED CAR INVENTORY’)
print(‘===================’)
# Display the car’s data
print(‘The following car is in inventory:’)
print(‘Make:’, car.get_make())
print(Model:’, car.get_model())
print(‘Mileage:’, car.get_mileage())
print(‘Price:’, car.get_price())
print(‘Number of doors:’, car.get_doors())
print()
# Display truck’s data
print(‘The following truck is in inventory:’)
print(‘Make:’, truck.get_make())
print(Model:’, truck.get_model())
print(‘Mileage:’, truck.get_mileage())
print(‘Price:’, truck.get_price())
print(‘Drive type:’, truck.get_drive_type())
print()
# Display SUV’s data
print(‘The following SUV is in inventory:’)
print(‘Make:’, suv.get_make())
print(Model:’, suv.get_model())
print(‘Mileage:’, suv.get_mileage())
print(‘Price:’, suv.get_price())
print(‘Passenger Capacity:’, suv.get_pass_cap())
print()
# Call the main function
main()- Inheritance in class diagram
- In Class diagram, show inheritance by drawing a line with an open arrowhead from subclass to superclass

- Polymorphism: an object’s ability to take different forms
- Methods in the subclasses have the same name as methods in the superclass but behave differently
- Essential ingredients of polymorphic behaviour:
- Ability to define a method in a superclass and override it in a subclass
- Subclass defines method with the same name
- Ability to call the correct version of overridden method depending on the type of object that is used to call it
- In previous inheritance examples showed how to override the
__init__method - Called superclass
__init__method and then added onto that - The same can be done for any other method. The method can call the superclass equivalent and add to it, or do something completely different
animals.py
# The Mammal class represents a generic mammal
class Mammal:
# The __init__ method accepts an argument for the mammal’s species
def __init__(self, species):
self._species = species
# The show_species method displays a message indicating the mammal’s species
def show_species(self):
print(‘I am a’, self._species)
# The make_sound method is the mammal’s way of making a generic sound
def make_sound(self):
print(‘Grrrrr’)
# The Dog class is a subclass of the Mammal class
class Dog(Mammal):
# The __init__ method calls the superclass’s __init__ method passing ‘Dog’ as the species
def __init__(self):
super().__init__(‘Dog’)
# The make_sound method overrides the superclass’s make_sound method
def make_sound(self):
print(‘Woof!’)
# The Cat class is a subclass of the Mammal class
class Cat(Mammal):
# The __init__ method calls the superclass’s __init__method passing ‘Cat’ as the species
def __init__(self):
super().__init__(‘Cat’)
#The make_sound method overrides the superclass’s make_sound method
def make_sound(self):
print(‘Meow’)# Code that creates a Mammal object, a Dog object, and calls the methods:
>>> import animals
>>> mammal = animals.Mammal(‘regular mammal’)
>>> mammal.show_species()
I am a regular mammal
>>> mammal.make_sound()
Grrrrr
>>> dog = animals.Dog()
>>> dog.show_species()
I am a Dog
>>> dog.make_sound()
Woof!
Mutator ⇒ set
Accessor ⇒ get
self._balance ⇒ _ is to show it is a private attribute, but it’s optional??
super().__init__(…) ⇒ inheritance
Comments from the Word document
# Code that creates a Mammal object, a Dog object, and calls the methods:
>>> import animals
>>> mammal = animals.Mammal(‘regular mammal’)
>>> mammal.show_species()
I am a regular mammal
>>> mammal.make_sound()
Grrrrr
>>> dog = animals.Dog()
>>> dog.show_species()
I am a Dog
>>> dog.make_sound()
Woof!
Mutator ⇒ set
Accessor ⇒ get
self._balance ⇒ _ is to show it is a private attribute, but it’s optional??
super().__init__(…) ⇒ inheritance