Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →

Python OOP Complete Guide 2026 — Object-Oriented Programming Mastery

Master Python object-oriented programming from basics to advanced. Classes, inheritance, polymorphism, SOLID principles, dataclasses — everything you need to write professional Python.

A
AiTechWorlds Team
May 11, 2026 8 min readUpdated May 15, 2026
📱

Get more content like this on Telegram!

Daily AI tips, notes & resources — free

Join Free →

Python OOP Complete Guide 2026 — Classes, Inheritance, and Beyond

Object-oriented programming is one of those concepts that feels abstract until suddenly it doesn't. Then you wonder how you ever organized code any other way.

OOP is about modeling your problem using objects — things that have both data (attributes) and behavior (methods). A BankAccount has a balance and can deposit/withdraw. A User has a name and email and can authenticate. Organizing code around these objects makes complex systems understandable.

This guide takes you from writing your first class to applying SOLID principles and using Python's most powerful OOP tools.


Why OOP?

# Without OOP — functions and data are unrelated
name = "Alice"
balance = 1000.0
transaction_history = []

def deposit(balance, amount):
    return balance + amount

def withdraw(balance, amount):
    if amount > balance:
        raise ValueError("Insufficient funds")
    return balance - amount

# Problems: all these variables are global, unrelated,
# and you can't have two accounts without duplicating everything

# With OOP — data and behavior live together
class BankAccount:
    def __init__(self, owner: str, initial_balance: float = 0):
        self.owner = owner
        self._balance = initial_balance
        self._history = []

    def deposit(self, amount: float) -> None:
        self._balance += amount
        self._history.append(f"+${amount:.2f}")

    def withdraw(self, amount: float) -> None:
        if amount > self._balance:
            raise ValueError("Insufficient funds")
        self._balance -= amount
        self._history.append(f"-${amount:.2f}")

    @property
    def balance(self) -> float:
        return self._balance

# Now you can have many accounts, each with its own state
alice = BankAccount("Alice", 1000)
bob = BankAccount("Bob", 500)

The Four Pillars of OOP

1. Encapsulation — Bundle Data and Methods

class Temperature:
    """Temperature with automatic unit conversion."""

    def __init__(self, celsius: float):
        self._celsius = celsius  # Private by convention (single underscore)

    @property
    def celsius(self) -> float:
        return self._celsius

    @celsius.setter
    def celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError(f"Temperature below absolute zero: {value}°C")
        self._celsius = value

    @property
    def fahrenheit(self) -> float:
        return self._celsius * 9/5 + 32

    @property
    def kelvin(self) -> float:
        return self._celsius + 273.15

    def __repr__(self) -> str:
        return f"Temperature({self._celsius}°C)"

temp = Temperature(100)
print(temp.celsius)     # 100
print(temp.fahrenheit)  # 212.0
print(temp.kelvin)      # 373.15

temp.celsius = -300     # Raises ValueError!

@property turns a method into an attribute-style access. Setters validate data before accepting it — this is encapsulation in action.

2. Inheritance — Reuse and Extend

class Animal:
    def __init__(self, name: str, species: str):
        self.name = name
        self.species = species

    def speak(self) -> str:
        raise NotImplementedError("Subclasses must implement speak()")

    def describe(self) -> str:
        return f"{self.name} is a {self.species}"

    def __str__(self) -> str:
        return f"{self.species}({self.name})"

class Dog(Animal):
    def __init__(self, name: str, breed: str):
        super().__init__(name, "Dog")  # Call parent __init__
        self.breed = breed

    def speak(self) -> str:
        return f"{self.name} says: Woof!"

    def fetch(self, item: str) -> str:
        return f"{self.name} fetches the {item}!"

class Cat(Animal):
    def __init__(self, name: str, indoor: bool = True):
        super().__init__(name, "Cat")
        self.indoor = indoor

    def speak(self) -> str:
        return f"{self.name} says: Meow!"

# Using the hierarchy
animals = [Dog("Rex", "Labrador"), Cat("Whiskers"), Dog("Buddy", "Beagle")]

for animal in animals:
    print(animal.speak())        # Polymorphism in action
    print(animal.describe())

3. Polymorphism — One Interface, Many Forms

from abc import ABC, abstractmethod
from typing import List

class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...

    @abstractmethod
    def perimeter(self) -> float: ...

    def describe(self) -> str:
        return f"{type(self).__name__}: area={self.area():.2f}, perimeter={self.perimeter():.2f}"

class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius

    def area(self) -> float:
        import math
        return math.pi * self.radius ** 2

    def perimeter(self) -> float:
        import math
        return 2 * math.pi * self.radius

class Rectangle(Shape):
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

    def perimeter(self) -> float:
        return 2 * (self.width + self.height)

class Triangle(Shape):
    def __init__(self, a: float, b: float, c: float):
        self.a, self.b, self.c = a, b, c

    def area(self) -> float:
        s = (self.a + self.b + self.c) / 2
        return (s * (s-self.a) * (s-self.b) * (s-self.c)) ** 0.5

    def perimeter(self) -> float:
        return self.a + self.b + self.c

# All shapes respond to the same interface
shapes: List[Shape] = [Circle(5), Rectangle(4, 6), Triangle(3, 4, 5)]

total_area = sum(s.area() for s in shapes)
for shape in shapes:
    print(shape.describe())
print(f"Total area: {total_area:.2f}")

4. Abstraction — Hide Complexity

Abstract base classes (ABC) define an interface without implementation. Subclasses must implement abstract methods — the ABC enforces the contract.

from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
    @abstractmethod
    def charge(self, amount: float, customer_id: str) -> str: ...

    @abstractmethod
    def refund(self, transaction_id: str) -> bool: ...

class StripeProcessor(PaymentProcessor):
    def charge(self, amount: float, customer_id: str) -> str:
        # Real Stripe API call here
        return f"stripe_txn_{customer_id}_{amount}"

    def refund(self, transaction_id: str) -> bool:
        # Real Stripe refund call here
        return True

class PayPalProcessor(PaymentProcessor):
    def charge(self, amount: float, customer_id: str) -> str:
        return f"paypal_txn_{customer_id}_{amount}"

    def refund(self, transaction_id: str) -> bool:
        return True

# Your business logic works with ANY payment processor
def process_order(processor: PaymentProcessor, amount: float, user_id: str) -> str:
    return processor.charge(amount, user_id)

Dataclasses — Modern Python OOP

Python 3.7+ dataclasses reduce boilerplate significantly:

from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, List

@dataclass
class User:
    name: str
    email: str
    age: int
    created_at: datetime = field(default_factory=datetime.utcnow)
    tags: List[str] = field(default_factory=list)
    is_active: bool = True

    def __post_init__(self):
        # Validation after __init__
        if self.age < 0:
            raise ValueError(f"Age cannot be negative: {self.age}")
        if "@" not in self.email:
            raise ValueError(f"Invalid email: {self.email}")
        self.email = self.email.lower()

@dataclass(frozen=True)  # Immutable dataclass
class Point:
    x: float
    y: float

    def distance_to(self, other: "Point") -> float:
        return ((self.x - other.x)**2 + (self.y - other.y)**2) ** 0.5

# Usage
user = User("Alice", "Alice@Example.com", 30)
print(user)  # User(name='Alice', email='alice@example.com', ...)

p1, p2 = Point(0, 0), Point(3, 4)
print(p1.distance_to(p2))  # 5.0

Dataclasses auto-generate __init__, __repr__, __eq__, and more.


Magic Methods (Dunder Methods)

class Vector:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    def __repr__(self) -> str:
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other: "Vector") -> "Vector":
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other: "Vector") -> "Vector":
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar: float) -> "Vector":
        return Vector(self.x * scalar, self.y * scalar)

    def __len__(self) -> int:
        return 2  # Always 2D

    def __abs__(self) -> float:
        return (self.x**2 + self.y**2) ** 0.5

    def __eq__(self, other) -> bool:
        if not isinstance(other, Vector):
            return NotImplemented
        return self.x == other.x and self.y == other.y

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)   # Vector(4, 6)
print(v2 - v1)   # Vector(2, 2)
print(abs(v2))   # 5.0
print(v1 * 3)    # Vector(3, 6)
print(v1 == Vector(1, 2))  # True

SOLID Principles in Python

PrincipleMeaningHow to Apply
Single ResponsibilityOne class, one jobIf a class does 3 things, split it into 3 classes
Open/ClosedOpen for extension, closed for modificationUse inheritance/composition instead of editing classes
Liskov SubstitutionSubclasses should replace their parentDon't override methods in ways that break the parent's contract
Interface SegregationMany small interfaces > one big oneSplit large ABCs into focused ones
Dependency InversionDepend on abstractions, not concrete classesInject dependencies, use ABCs
# Single Responsibility Principle
# BAD — User class does too many things
class UserBad:
    def save_to_db(self): ...
    def send_welcome_email(self): ...
    def generate_pdf_report(self): ...
    def authenticate(self): ...

# GOOD — each class has one responsibility
class User:
    def authenticate(self): ...

class UserRepository:
    def save(self, user: User): ...
    def find_by_id(self, user_id: int) -> User: ...

class EmailService:
    def send_welcome(self, user: User): ...

class ReportGenerator:
    def user_report_pdf(self, user: User) -> bytes: ...

Practical OOP: Building a Mini E-commerce System

from dataclasses import dataclass, field
from typing import List
from abc import ABC, abstractmethod
from datetime import datetime

@dataclass
class Product:
    id: int
    name: str
    price: float
    stock: int

    def is_available(self) -> bool:
        return self.stock > 0

@dataclass
class OrderItem:
    product: Product
    quantity: int

    @property
    def subtotal(self) -> float:
        return self.product.price * self.quantity

@dataclass
class Order:
    customer_id: int
    items: List[OrderItem] = field(default_factory=list)
    created_at: datetime = field(default_factory=datetime.utcnow)
    status: str = "pending"

    @property
    def total(self) -> float:
        return sum(item.subtotal for item in self.items)

    def add_item(self, product: Product, quantity: int = 1) -> None:
        if not product.is_available():
            raise ValueError(f"{product.name} is out of stock")
        if product.stock < quantity:
            raise ValueError(f"Only {product.stock} units available")
        self.items.append(OrderItem(product=product, quantity=quantity))

# Usage
laptop = Product(1, "MacBook Pro", 2499.99, 5)
mouse = Product(2, "Magic Mouse", 79.99, 20)

order = Order(customer_id=42)
order.add_item(laptop, quantity=1)
order.add_item(mouse, quantity=2)

print(f"Order total: ${order.total:.2f}")  # $2659.97

OOP shines when systems grow complex. For applying these patterns in a web context, see the FastAPI tutorial — Pydantic models are a form of dataclasses on steroids. For understanding more advanced Python patterns built on top of OOP, read the decorators and generators guide.

The best way to internalize OOP is to redesign something you have already built using classes. Take a messy script with many global variables and refactor it. The clarity you gain will change how you write Python forever.

Python OOP design patterns and SOLID principle examples available in the AiTechWorlds Telegram channel!

Share this article:

Frequently Asked Questions

Yes — Python supports full OOP with classes, inheritance, polymorphism, and encapsulation. It also supports multiple programming paradigms, so you can mix OOP with functional programming.
A

AiTechWorlds Team

✓ Verified Writer

The AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.

Related Articles

10K+ Members Growing Daily

Get Free AI Notes Daily

Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!

📚 Free Study Notes🤖 AI Tips Daily⚡ Prompt Templates💻 Coding Resources
Join Free Channel

No spam. Leave anytime.

!