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.