← Back to Tutorials Chapter 11

Object-Oriented Programming (OOP)

Python OOP class hierarchy

Classes and Objects

A class is a blueprint for creating objects. In Python, everything is an object, and classes define the structure and behavior of those objects.

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        return f"{self.name} says woof!"

my_dog = Dog("Rex", 3)
print(my_dog.bark())

The __init__ Method and self

The __init__ method is the constructor, called automatically when a new instance is created. The self parameter refers to the current instance.

Instance vs Class Attributes

class Car:
    wheels = 4  # class attribute

    def __init__(self, make, model):
        self.make = make  # instance attribute
        self.model = model

Instance, Class, and Static Methods

class MathUtils:
    @classmethod
    def from_string(cls, text):
        return cls()

    @staticmethod
    def add(a, b):
        return a + b

Property Decorators

The @property decorator allows you to define methods that can be accessed like attributes, with getter, setter, and deleter.

class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius

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

    @fahrenheit.setter
    def fahrenheit(self, value):
        self._celsius = (value - 32) * 5 / 9

__str__ and __repr__

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __repr__(self):
        return f"Book('{self.title}', '{self.author}')"

    def __str__(self):
        return f"{self.title} by {self.author}"

Inheritance and MRO

Inheritance allows a class to derive attributes and methods from a parent class. Python uses the C3 linearization algorithm for Method Resolution Order (MRO).

class Animal:
    def speak(self):
        return "Some sound"

class Cat(Animal):
    def speak(self):
        return "Meow"

print(Cat().speak())  # Meow
print(Cat.__mro__)    # Method resolution order

super()

class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)

Polymorphism and Method Overriding

Polymorphism lets different classes define methods with the same name. Method overriding allows a subclass to provide a specific implementation of a method already defined in its parent class.

for animal in [Dog(), Cat()]:
    print(animal.speak())

Encapsulation

Python uses naming conventions for access control: _protected (single underscore) and __private (double underscore triggers name mangling).

class BankAccount:
    def __init__(self):
        self.balance = 0         # public
        self._fee = 0.01         # protected
        self.__pin = "1234"      # private

Abstract Classes (ABC)

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

Dataclasses

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

    def distance_from_origin(self):
        return (self.x**2 + self.y**2) ** 0.5

Enum

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3
Note: Python supports multiple inheritance. Use it carefully to avoid the diamond problem.
Best Practice: Favor composition over inheritance when possible. Use ABCs to define interfaces.
Exercise: Create a Library class that stores Book objects. Implement methods to add, remove, search by title, and list all books. Use a dataclass for Book with fields: title, author, year, isbn.