Intermediate⏱ 35 min

Classes & OOP

Object-Oriented Programming lets you model the world as objects with state and behaviour. Master classes and you unlock the design patterns behind every major Python library.

What is a Class?

A class is a blueprint. An object (instance) is something built from that blueprint. You can make as many objects from one class as you want, each with its own data.

python
class Dog:
    # __init__ is the constructor — runs when you create an instance
    def __init__(self, name: str, breed: str, age: int):
        self.name = name    # instance attributes — unique per object
        self.breed = breed
        self.age = age

    def bark(self):         # instance method
        return f"{self.name} says: Woof!"

    def info(self):
        return f"{self.name} ({self.breed}), {self.age} yr old"

    def __repr__(self):     # what prints when you inspect the object
        return f"Dog(name={self.name!r}, breed={self.breed!r})"

# Create instances
rex  = Dog("Rex",  "Labrador", 3)
luna = Dog("Luna", "Poodle",   5)

print(rex.bark())    # Rex says: Woof!
print(luna.info())   # Luna (Poodle), 5 yr old
print(rex)           # Dog(name='Rex', breed='Labrador')
print(rex.name)      # Rex
Output
🔑
What is self?self is a reference to the current instance. When you call rex.bark(), Python silently passes rex as the first argument — that's self.

Class Attributes and Class Methods

python
class BankAccount:
    interest_rate = 0.03   # class attribute — shared by all instances

    def __init__(self, owner: str, balance: float = 0):
        self.owner   = owner
        self.balance = balance

    def deposit(self, amount: float):
        self.balance += amount
        return self.balance

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

    def apply_interest(self):
        self.balance *= (1 + BankAccount.interest_rate)

    @classmethod
    def from_dict(cls, data: dict):
        """Alternative constructor"""
        return cls(data["owner"], data.get("balance", 0))

    @staticmethod
    def is_valid_amount(amount):
        return isinstance(amount, (int, float)) and amount > 0

acc = BankAccount("Alice", 1000)
acc.deposit(500)
acc.apply_interest()
print(f"Balance: ${acc.balance:.2f}")   # 1545.00

acc2 = BankAccount.from_dict({"owner": "Bob", "balance": 200})
print(acc2.owner, acc2.balance)
print(BankAccount.is_valid_amount(-5))  # False
Output

Inheritance

A child class inherits all attributes and methods from its parent. It can override them or add new ones.

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

    def speak(self):
        return f"{self.name} makes a sound"

    def __str__(self):
        return f"{type(self).__name__}({self.name!r})"

class Dog(Animal):
    def speak(self):                   # override
        return f"{self.name}: Woof!"

    def fetch(self, item: str):        # new method
        return f"{self.name} fetches the {item}!"

class Cat(Animal):
    def speak(self):
        return f"{self.name}: Meow!"

    def purr(self):
        return f"{self.name} purrs..."

animals = [Dog("Rex"), Cat("Whiskers"), Dog("Buddy")]

for animal in animals:
    print(animal.speak())              # polymorphism!

print(isinstance(animals[0], Dog))    # True
print(isinstance(animals[0], Animal)) # True — Dog IS-A Animal
Output

Dunder Methods (Magic Methods)

Methods with double underscores like __len__, __add__, __eq__ let your objects behave like built-in types.

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

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

    def __add__(self, other):       # v1 + v2
        return Vector(self.x + other.x, self.y + other.y)

    def __mul__(self, scalar):      # v * 3
        return Vector(self.x * scalar, self.y * scalar)

    def __eq__(self, other):        # v1 == v2
        return self.x == other.x and self.y == other.y

    def __len__(self):              # len(v)
        return 2                    # a 2D vector has 2 components

v1 = Vector(1, 2)
v2 = Vector(3, 4)

print(v1 + v2)        # Vector(4, 6)
print(v1 * 3)         # Vector(3, 6)
print(v1 == Vector(1, 2))  # True
print(len(v1))        # 2
Output
🎉

Lesson complete!

Next: Error Handling — writing robust, crash-resistant code.

🏆

Certificate Unlocked!

You completed all lessons with 70%+. View your certificate →