Object Oriented Programming(OOP) is a way of writing programs by modelling real-world things as "objects". Instead of writing code as a long list of instructions that act on separate variables, OOP groups related data(attributes) and behaviour (methods) together into a single object called an object.
Real world Analogy
Think about a real world car.
A car has attributes(color, fuel type, brand, max-speed) it can do things like(accelerate, braking, honk).
In OOP we would create a Car object that bundles those attributes and behaviours together, instead of tracking color, brand, fuel type, max-speed acceleration,braking etc as disconnected variables.
Four Pillars of OOP
- Encapsulation
- Inheritance
- Polymorphism
- Abstraction
Classes and Object
What is a class?
- A class is a blueprint or template for creating objects. It defines what attributes(data) and methods(functions) the objects created from it will have. Note: A class does not hold any actual data itself, it only describes the shape that objects of that type will take.
a. Class Attributes
Defined directly inside the class body, outside of any method(function). They are shared by all objects of that class unless an individual object overrides them.
Example
class Dog:
species = "Canis familiaris" # class attribute (Shared by all Dogs)
b. Instance attributes
Defined inside init(or any method) using self.attribute_name = value.
Each object gets its own independent copy.
class Dog:
def __init__(self, name, breed):
self.name = name # Instance attribute, unique per dog/instance of the class
self.breed = breed # Instance attribute, unique per dog/instance of the class
Methods
- Methods are functions defined inside a class. They define/describe the behaviours an object can perform. Like init, regular methods take 'self' as their first parameter so they can access and modify the object's own attributes.
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def bark(self):
print(f"{self.name} says: Woof!")
def play(self, minutes):
print(f"{self.name} played for {minutes} minutes")
def fact(self, top_speed):
print(f"The dog '{self.name}' of breed '{self.breed}' can reach a maximum speed of {top_speed}km/h.")
dog1 = Dog("Bosco", "African Shepherd")
dog1.bark()
dog1.play(30)
dog1.fact(72)
Output:
Bosco says: Woof!
Bosco played for 30 minutes
The dog 'Bosco' of breed 'African Shepherd' can reach a maximum speed of 72km/h.
# German Shepherd Instance
german_shepherd = Dog("Max", "German Shepherd")
german_shepherd.bark()
german_shepherd.play(60)
german_shepherd.fact(56)
Output:
Max says: Woof!
Max played for 60 minutes
The dog 'Max' of breed 'German Shepherd' can reach a maximum speed of 56km/h.
MPESA Example
class MpesaAccount:
def __init__(self, phone_number, owner_name, balance):
self.phone_number = phone_number
self.owner_name = owner_name
self.balance = balance
def deposit(self, amount):
self.balance += amount # self.balance = self.balance + amount
print(f"You have successfully deposited KES {amount}, new acc balance KES {self.balance}")
def withdraw(self, amount):
self.balance -= amount # self.balance = self.balance - amount
print(f"You have successfully withdrawn KES {amount}, new acc balance KES {self.balance}")
def check_balance(self):
print(f"Balance: KES {self.balance}")
emmanuel_acc = MpesaAccount("+254712345678", "Emmanuel", 0)
emmanuel_acc.deposit(500)
Output:
You have successfully deposited KES 500, new acc balance KES 500
emmanuel_acc.withdraw(250)
Output:
You have successfully withdrawn KES 250, new acc balance KES 250
emmanuel_acc.check_balance()
Output:
Balance: KES 250
Four Pillars Of Object Oriented Programming
1. Encapsulation
- Encapsulation means bundling an object's data(attributes) and the methods that operate on that data into a single class, and restricting direct access to some of that data from outside the class. This protects the object's internal state from being change in unexpected or invalid ways.
In Python encapsulation is a convention rather a strict:
- name - public, accessible from anywhere.
- _name - protected, a signal to other developers "Don't touch this directly".
- __name - private, Python renames it internally(name mangling) to discourage outside access.
class MpesaAccount:
def __init__(self, owner_name, phone_number, balance=0):
self.owner_name = owner_name
self.phone_number = phone_number
self.__balance = balance
def deposit(self, amount):
if amount <= 49:
print("Deposit must be a minimum of KES 50.")
else:
self.__balance += amount
print(f"You have successfully deposited {amount} ne acc bal is KES {self.__balance}")
def withdraw(self, amount):
if amount > self.__balance:
print("Insufficient Funds")
else:
self.__balance -= amount
print(f"You have successfully withdrawn {amount} ne acc bal is KES {self.__balance}")
def check_balance(self):
return f"You account balance is KES {self.__balance}"
emmanuel_mpesa = MpesaAccount("Emm", "0701000000")
emmanuel_mpesa.deposit(150)
Output:
You have successfully deposited 150 ne acc bal is KES 150
2. Inheritance
- Inheritance allows a new class to(child class/subclass/derived class) reuse the attributes and methods of an existing class(parent class/super class/base class).
# Parent Class
class PublicService:
def __init__(self, reg_number, route):
self.reg_number = reg_number
self.route = route
def describe(self):
return f"Vehicle registaration number {self.reg_number} plies the {self.route} route."
def collect_fare(self, amount):
return f"KES {amount} fare collected on {self.reg_number}."
# Child class 1
class Matatu(PublicService):
def __init__(self, reg_number, route, sacco_name, capacity):
super().__init__(reg_number, route)
self.sacco_name = sacco_name
self.capacity = capacity
def describe(self):
return f"Vehicle registaration number {self.reg_number}, of the sacco {self.sacco_name} plies the {self.route} route."
kabuku = Matatu("KDQ 221Z", "Nairobi-Thika", "Super-Metro", 33)
kabuku.collect_fare(100)
Output:
'KES 100 fare collected on KDQ 221Z.'
# Child Class 2
class BodaBoda(PublicService):
def __init__(self, reg_number, route, rider_name):
super().__init__(reg_number, route)
self.rider_name = rider_name
# def describe(self):
# return f"Boda Boda {self.reg_number}, rider {self.rider_name} is on the {self.route} route"
bodaboda = BodaBoda("KMZT 300Z", "CBD-Upper-Hill", "Emmanuel")
bodaboda.describe()
Output:
'Vehicle registaration number KMZT 300Z plies the CBD-Upper-Hill route.'
Multi-level inheritance
- Multilevel inheritance in Python is an object-oriented programming feature where a derived (child) class inherits from another derived class, forming a sequential, multi-generational chain (e.g., Grandparent → Parent → Child). This structure allows the deepest child class to automatically access all attributes and methods across the entire ancestor hierarchy
- Also inherited attributes and methods without super() function - another way of inheriting attributes and methods besides using super() function.
class company:
title:str = "consultancy"
def __init__(self, company_name:str):
self.company_name:str = company_name
def info(self):
print(f"Company Name: {self.company_name}")
return f"Company Name: {self.company_name}"
class manager(company):
def __init__(self, manager_name:str, company_name:str):
self.manager_name = manager_name
self.company_name = company_name
def info(self):
response = company.info(self)
print(f"The manager : {self.manager_name}, {response} ")
return f"The manager : {self.manager_name}, {response} "
class employee(manager):
def __init__(self,employee_name:str, manager_name:str, company_name:str):
self.employee_name = employee_name
self.manager_name = manager_name
self.company_name = company_name
def info(self):
response = manager.info(self)
print(f"The employee : {self.employee_name}, {response} ")
return f"The employee : {self.employee_name}, {response} "
obj = employee("John Doe", "Alice Johnson", "Tech Solutions")
obj.info()
Multiple Inheritance
- Multiple inheritance is an object-oriented programming feature where a class inherits behaviors and attributes from more than one parent class
class company:
title:str = "consultancy"
def __init__(self, company_name:str):
self.company_name:str = company_name
def info(self):
print(f"Company Name: {self.company_name}")
return f"Company Name: {self.company_name}"
class employee(company):
def __init__(self,employee_name:str, company_name:str):
self.employee_name = employee_name
self.company_name = company_name
def info(self):
response = company.info(self)
print(f"The employee : {self.employee_name}, {response} ")
class contractor(company):
def __init__(self, contractor_name:str, company_name:str):
self.contractor_name = contractor_name
self.company_name = company_name
def info(self):
response = company.info(self)
print(f"The contractor : {self.contractor_name}, {response} ")
obj = employee("John Doe", "Tech Solutions")
obj.info()
obj2 = contractor("Jane Smith", "Tech Solutions")
obj2.info()
3. Polymorphism
- Polymorphism(Greek word for "many forms") means objects of different classes can respond to the same method call in their own way. You call the same method name on different objects, and each one behaves according to its own class's implementation.
Python supports polymorphism naturally through method overriding(child classses redefine a parent's method as seen in the Delivery() and VanDelivery()).
4. Abstraction
- Abstraction means hiding complicated implementation details and exposing only the essential features that a user of the class actually needs. IN python, abstraction is commonly implemented using the abc module to define abstract base classes. (Classes that define required methods but cannot be instantiated directly, forcing the subclasses/child classes to provide the actual implentation)
from abc import ABC, abstractmethod
# Parent Class
class Shape(ABC): # Inherits from ABC, it cannot be instantiated
@abstractmethod
def area(self):
pass # No implementation on purpose
@abstractmethod
def perimeter(self):
pass # No implementation on purpose
# Child Class
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
area = self.width * self.height
return area
def perimeter(self):
perimeter = 2 * (self.height + self.width)
return perimeter
rect = Rectangle(10, 6)
rect.area()
Output:
60
rectange = Shape()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[69], line 1
----> 1 rectange = Shape()
TypeError: Can't instantiate abstract class Shape without an implementation for abstract methods 'area', 'perimeter'
Conclusion
Object-Oriented Programming (OOP) in Python is a powerful paradigm that shifts the programming focus from functions to structured objects, making software more modular, scalable, and maintainable. Because "everything in Python is an object," OOP is not just an optional tool; it forms the core DNA of the language and its most popular libraries.
Top comments (0)