🧠 Complex Object-Oriented Programming (OOP) in Python
Author: Shaukat
Object-Oriented Programming (OOP) is not just about syntax — it's a philosophy, a design methodology that helps you model real-world entities in software. Let’s move beyond the basics and deeply explore how OOP scales from simple use to complex patterns.
🌱 Basics: Class & Object (with Theory)
Theory: A class is a blueprint. An object is an instance of that blueprint. You use classes when you want to model entities with state (attributes) and behavior (methods).
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"Hello, I’m {self.name}, {self.age} years old.")
shaukat = Person("Shaukat", 25)
shaukat.greet()🧱 Encapsulation, Abstraction, Inheritance, Polymorphism
📦 Encapsulation
Theory: Encapsulation hides internal state and forces interaction through an object's methods. This ensures data integrity and secure APIs.
class BankAccount:
def __init__(self, owner):
self.__balance = 0
self.owner = owner
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def get_balance(self):
return self.__balance
shaukat_account = BankAccount("Shaukat")
shaukat_account.deposit(1000)
print(shaukat_account.get_balance())🧊 Abstraction
Theory: Abstraction lets you expose only relevant functionality, hiding internal workings. Abstract Base Classes (ABCs) enforce contracts.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
circle = Circle(5)
print(circle.area())🧬 Inheritance
Theory: Inheritance enables reuse and hierarchical relationships. It supports DRY (Don't Repeat Yourself) principles.
class Employee:
def __init__(self, name):
self.name = name
def work(self):
return f"{self.name} is working."
class Developer(Employee):
def work(self):
return f"{self.name} is writing code."
shaukat_dev = Developer("Shaukat")
print(shaukat_dev.work())🎭 Polymorphism
Theory: Polymorphism allows objects of different types to be treated uniformly through a shared interface.
def describe_worker(worker):
print(worker.work())
dev = Developer("Shaukat")
emp = Employee("Ali")
describe_worker(dev)
describe_worker(emp)🔮 Advanced Concepts
1. Classmethod vs Staticmethod vs Instance Method
class App:
app_count = 0
def __init__(self):
App.app_count += 1
@classmethod
def get_count(cls):
return cls.app_count
@staticmethod
def info():
return "This is a general-purpose app."
print(App.get_count())
print(App.info())Theory:
@classmethodmodifies class-level state.@staticmethodis detached logic that doesn't access instance/class state.- Instance methods act on object-specific data.
2. Composition over Inheritance
Theory: Composition provides greater flexibility than inheritance. You build objects using other objects instead of creating deep hierarchies.
class Engine:
def start(self):
print("Engine starting...")
class Car:
def __init__(self):
self.engine = Engine()
def drive(self):
self.engine.start()
print("Car is moving")
shaukat_car = Car()
shaukat_car.drive()🧩 Design Patterns (Applied OOP)
📐 1. Singleton Pattern
Significance: Guarantees a single instance globally (used in logging, DB connections).
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2) # True📦 2. Factory Pattern
Significance: Abstracts object creation, useful when classes are chosen based on runtime logic.
class Notification:
def notify(self):
pass
class Email(Notification):
def notify(self):
print("Email sent")
class SMS(Notification):
def notify(self):
print("SMS sent")
def notification_factory(type):
if type == "email":
return Email()
return SMS()
note = notification_factory("email")
note.notify()🚀 Extremely Advanced Patterns
🎯 Strategy Pattern
Significance: Replaces conditional logic with interchangeable behaviors at runtime.
class SortStrategy:
def sort(self, data): pass
class BubbleSort(SortStrategy):
def sort(self, data):
return sorted(data)
class QuickSort(SortStrategy):
def sort(self, data):
return sorted(data, reverse=True)
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)
shaukat_sorter = Sorter(BubbleSort())
print(shaukat_sorter.sort_data([3, 2, 1]))🕵️♂️ Observer Pattern
Significance: Enables reactive programming. Great for GUIs, game engines, etc.
class Subject:
def __init__(self):
self._observers = []
def register(self, observer):
self._observers.append(observer)
def notify(self):
for obs in self._observers:
obs.update()
class Listener:
def update(self):
print("Notified!")
sub = Subject()
obs1 = Listener()
sub.register(obs1)
sub.notify()🧠 Adding SOLID Principles and UML Diagrams
To truly master OOP, you must also:
- Understand SOLID principles (Single Responsibility, Open/Closed, Liskov, Interface Segregation, Dependency Inversion)
- Draw UML Class Diagrams to visualize architecture before coding
These practices help in designing flexible, extensible, and maintainable software systems — the kind that scale and survive over time.
🏗 Additional Patterns & Meta Programming
🧰 Decorator Pattern
Significance: Add new behavior to objects at runtime without modifying their code.
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
class Calculator:
@log_function_call
def add(self, x, y):
return x + y
shaukat_calc = Calculator()
print(shaukat_calc.add(3, 4))🧬 Metaclasses
Theory: Metaclasses let you control class creation — useful in frameworks (like Django).
class Meta(type):
def __new__(cls, name, bases, attrs):
attrs['id'] = 1
return super().__new__(cls, name, bases, attrs)
class MyClass(metaclass=Meta):
pass
print(MyClass.id) # Output: 1📘 Conclusion
OOP is not just syntax — it’s a design mindset. Understanding why and how each pattern works will help you:
- Avoid tightly coupled code
- Write reusable modules
- Enhance maintainability
- Enable testability and scalability
Whether you’re implementing a Singleton for a config, using Strategy to choose algorithms dynamically, or building reactive systems with Observer — mastering these patterns is crucial for professional development.
Use OOP intentionally. That’s the path