Skip to content

Object-Oriented Programming ​

πŸ—οΈ Classes and Objects ​

Creating a Class ​

python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def greet(self):
        return f"Hello, my name is {self.name}"
    
    def have_birthday(self):
        self.age += 1
        return f"Happy birthday! I'm now {self.age} years old"

# Creating objects (instances)
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)

print(person1.greet())  # Hello, my name is Alice
print(person2.have_birthday())  # Happy birthday! I'm now 31 years old

Instance vs Class Variables ​

python
class Student:
    # Class variable (shared by all instances)
    school = "Python Academy"
    
    def __init__(self, name, grade):
        # Instance variables (unique to each instance)
        self.name = name
        self.grade = grade
    
    def study(self):
        return f"{self.name} is studying at {self.school}"

student1 = Student("Alice", "A")
student2 = Student("Bob", "B")

print(student1.study())  # Alice is studying at Python Academy
print(Student.school)    # Python Academy

πŸ”’ Encapsulation ​

Private Attributes ​

python
class BankAccount:
    def __init__(self, account_number, initial_balance=0):
        self.account_number = account_number
        self._balance = initial_balance  # Protected attribute
        self.__pin = None  # Private attribute
    
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            return f"Deposited ${amount}. New balance: ${self._balance}"
        return "Invalid deposit amount"
    
    def withdraw(self, amount):
        if 0 < amount <= self._balance:
            self._balance -= amount
            return f"Withdrew ${amount}. New balance: ${self._balance}"
        return "Invalid withdrawal amount or insufficient funds"
    
    def get_balance(self):
        return self._balance
    
    def set_pin(self, pin):
        self.__pin = pin
    
    def _validate_pin(self, pin):
        return self.__pin == pin

account = BankAccount("12345", 1000)
print(account.deposit(500))  # Deposited $500. New balance: $1500
print(account.get_balance())  # 1500

Properties (Getters and Setters) ​

python
class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def radius(self):
        return self._radius
    
    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise ValueError("Radius must be positive")
        self._radius = value
    
    @property
    def area(self):
        return 3.14159 * self._radius ** 2
    
    @property
    def circumference(self):
        return 2 * 3.14159 * self._radius

circle = Circle(5)
print(circle.radius)  # 5
print(circle.area)    # 78.53975
circle.radius = 10
print(circle.area)    # 314.159

🧬 Inheritance ​

Basic Inheritance ​

python
class Animal:
    def __init__(self, name, species):
        self.name = name
        self.species = species
    
    def speak(self):
        return f"{self.name} makes a sound"
    
    def info(self):
        return f"{self.name} is a {self.species}"

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name, "Dog")
        self.breed = breed
    
    def speak(self):
        return f"{self.name} barks"
    
    def fetch(self):
        return f"{self.name} fetches the ball"

class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name, "Cat")
        self.color = color
    
    def speak(self):
        return f"{self.name} meows"
    
    def purr(self):
        return f"{self.name} purrs contentedly"

# Usage
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers", "Orange")

print(dog.speak())  # Buddy barks
print(cat.speak())  # Whiskers meows
print(dog.info())   # Buddy is a Dog
print(dog.fetch())  # Buddy fetches the ball

Multiple Inheritance ​

python
class Flyable:
    def fly(self):
        return "Flying through the air"

class Swimmable:
    def swim(self):
        return "Swimming in water"

class Duck(Animal, Flyable, Swimmable):
    def __init__(self, name):
        super().__init__(name, "Duck")
    
    def speak(self):
        return f"{self.name} quacks"

duck = Duck("Donald")
print(duck.speak())  # Donald quacks
print(duck.fly())    # Flying through the air
print(duck.swim())   # Swimming in water

πŸ”„ Polymorphism ​

Method Overriding ​

python
class Shape:
    def area(self):
        raise NotImplementedError("Subclass must implement area method")
    
    def perimeter(self):
        raise NotImplementedError("Subclass must implement perimeter method")

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14159 * self.radius ** 2
    
    def perimeter(self):
        return 2 * 3.14159 * self.radius

# Polymorphism in action
shapes = [Rectangle(4, 5), Circle(3), Rectangle(2, 8)]

for shape in shapes:
    print(f"Area: {shape.area():.2f}")
    print(f"Perimeter: {shape.perimeter():.2f}")
    print()

πŸ”§ Special Methods (Magic Methods) ​

Common Magic Methods ​

python
class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
    
    def __str__(self):
        return f"'{self.title}' by {self.author}"
    
    def __repr__(self):
        return f"Book('{self.title}', '{self.author}', {self.pages})"
    
    def __len__(self):
        return self.pages
    
    def __eq__(self, other):
        if isinstance(other, Book):
            return self.title == other.title and self.author == other.author
        return False
    
    def __lt__(self, other):
        if isinstance(other, Book):
            return self.pages < other.pages
        return NotImplemented
    
    def __add__(self, other):
        if isinstance(other, Book):
            return self.pages + other.pages
        return NotImplemented

book1 = Book("Python Programming", "John Doe", 300)
book2 = Book("Data Science", "Jane Smith", 450)

print(str(book1))    # 'Python Programming' by John Doe
print(repr(book1))   # Book('Python Programming', 'John Doe', 300)
print(len(book1))    # 300
print(book1 < book2) # True
print(book1 + book2) # 750

🏭 Class Methods and Static Methods ​

python
class Employee:
    company = "Tech Corp"
    employee_count = 0
    
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
        Employee.employee_count += 1
    
    # Instance method
    def get_info(self):
        return f"{self.name} earns ${self.salary}"
    
    # Class method
    @classmethod
    def get_employee_count(cls):
        return cls.employee_count
    
    @classmethod
    def from_string(cls, employee_str):
        name, salary = employee_str.split('-')
        return cls(name, int(salary))
    
    # Static method
    @staticmethod
    def is_workday(day):
        return day.lower() not in ['saturday', 'sunday']

# Usage
emp1 = Employee("Alice", 70000)
emp2 = Employee("Bob", 80000)

print(Employee.get_employee_count())  # 2

# Create employee from string
emp3 = Employee.from_string("Charlie-75000")
print(emp3.get_info())  # Charlie earns $75000

# Static method usage
print(Employee.is_workday("Monday"))  # True
print(Employee.is_workday("Saturday"))  # False

πŸ›οΈ Abstract Classes ​

python
from abc import ABC, abstractmethod

class Vehicle(ABC):
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
    
    @abstractmethod
    def start_engine(self):
        pass
    
    @abstractmethod
    def stop_engine(self):
        pass
    
    def info(self):
        return f"{self.brand} {self.model}"

class Car(Vehicle):
    def start_engine(self):
        return "Car engine started with key"
    
    def stop_engine(self):
        return "Car engine stopped"

class Motorcycle(Vehicle):
    def start_engine(self):
        return "Motorcycle engine started with button"
    
    def stop_engine(self):
        return "Motorcycle engine stopped"

# Usage
car = Car("Toyota", "Camry")
motorcycle = Motorcycle("Honda", "CBR")

print(car.info())  # Toyota Camry
print(car.start_engine())  # Car engine started with key
print(motorcycle.start_engine())  # Motorcycle engine started with button

πŸš€ Practice Examples ​

Example 1: Library Management System ​

python
class Book:
    def __init__(self, title, author, isbn):
        self.title = title
        self.author = author
        self.isbn = isbn
        self.is_checked_out = False
        self.checked_out_by = None
    
    def __str__(self):
        return f"'{self.title}' by {self.author}"

class Member:
    def __init__(self, name, member_id):
        self.name = name
        self.member_id = member_id
        self.checked_out_books = []
    
    def check_out_book(self, book):
        if not book.is_checked_out:
            book.is_checked_out = True
            book.checked_out_by = self
            self.checked_out_books.append(book)
            return f"{self.name} checked out {book}"
        return f"{book} is already checked out"
    
    def return_book(self, book):
        if book in self.checked_out_books:
            book.is_checked_out = False
            book.checked_out_by = None
            self.checked_out_books.remove(book)
            return f"{self.name} returned {book}"
        return f"{book} was not checked out by {self.name}"

class Library:
    def __init__(self, name):
        self.name = name
        self.books = []
        self.members = []
    
    def add_book(self, book):
        self.books.append(book)
    
    def add_member(self, member):
        self.members.append(member)
    
    def find_book(self, title):
        for book in self.books:
            if book.title.lower() == title.lower():
                return book
        return None
    
    def list_available_books(self):
        return [book for book in self.books if not book.is_checked_out]

# Usage
library = Library("City Library")
book1 = Book("Python Programming", "John Doe", "123456")
book2 = Book("Data Science", "Jane Smith", "789012")

library.add_book(book1)
library.add_book(book2)

member1 = Member("Alice", "M001")
library.add_member(member1)

print(member1.check_out_book(book1))  # Alice checked out 'Python Programming' by John Doe
print(len(library.list_available_books()))  # 1

Example 2: Game Character System ​

python
class Character:
    def __init__(self, name, health, attack_power):
        self.name = name
        self.max_health = health
        self.health = health
        self.attack_power = attack_power
        self.level = 1
    
    def attack(self, target):
        damage = self.attack_power
        target.take_damage(damage)
        return f"{self.name} attacks {target.name} for {damage} damage"
    
    def take_damage(self, damage):
        self.health -= damage
        if self.health <= 0:
            self.health = 0
            return f"{self.name} has been defeated"
        return f"{self.name} takes {damage} damage, health: {self.health}"
    
    def heal(self, amount):
        self.health = min(self.max_health, self.health + amount)
        return f"{self.name} heals for {amount}, health: {self.health}"
    
    def is_alive(self):
        return self.health > 0

class Warrior(Character):
    def __init__(self, name):
        super().__init__(name, health=120, attack_power=25)
        self.armor = 10
    
    def defend(self):
        return f"{self.name} raises shield, reducing next damage by 50%"
    
    def take_damage(self, damage):
        reduced_damage = max(1, damage - self.armor)
        return super().take_damage(reduced_damage)

class Mage(Character):
    def __init__(self, name):
        super().__init__(name, health=80, attack_power=15)
        self.mana = 50
    
    def cast_spell(self, target):
        if self.mana >= 20:
            self.mana -= 20
            spell_damage = self.attack_power * 2
            target.take_damage(spell_damage)
            return f"{self.name} casts fireball on {target.name} for {spell_damage} damage"
        return f"{self.name} doesn't have enough mana"

# Usage
warrior = Warrior("Sir Lancelot")
mage = Mage("Gandalf")

print(warrior.attack(mage))  # Sir Lancelot attacks Gandalf for 25 damage
print(mage.cast_spell(warrior))  # Gandalf casts fireball on Sir Lancelot for 30 damage
print(warrior.health)  # 105 (120 - 30 + 10 armor = 105)

🎯 Key Takeaways ​

  • Classes are blueprints for creating objects
  • Objects are instances of classes with their own data
  • Encapsulation hides internal details and provides controlled access
  • Inheritance allows classes to inherit from other classes
  • Polymorphism enables objects to be treated as instances of their parent class
  • Magic methods customize object behavior for built-in operations
  • Class methods operate on the class itself, not instances
  • Static methods are utility functions that belong to the class
  • Abstract classes define interfaces that subclasses must implement
  • OOP promotes code reusability, modularity, and maintainability

Continue to: Built-in Functions β†’

Released under the MIT License.