OOP Basics

🏗️ Building with Blueprints: Classes, Objects & Methods

Master the art of organizing code with classes and objects for scalable, maintainable programs

🧠 Mental Model: Architectural Blueprints

The House Blueprint Metaphor

Think of a class like an architectural blueprint for a house. The blueprint defines what every house built from it will have (rooms, doors, windows), but it's not a house itself. When builders use the blueprint to construct actual houses, each house is an "object" - a real instance with specific features.

  • 🏗️ Class - The blueprint (template) defining structure and behavior
  • 🏠 Objects - Actual houses built from the blueprint (instances)
  • 🔧 Methods - Actions the house can perform (turn on lights, lock doors)
  • 📋 Attributes - Properties of each house (color, size, address)

📋 Class Blueprint

class House:
    def __init__(self, color, size):
        self.color = color
        self.size = size
        self.lights_on = False
    
    def turn_on_lights(self):
        self.lights_on = True

🏠 Object Instances

House 1: Blue, Large
House 2: Red, Small
House 3: Green, Medium

🏗️ Classes: Creating Your Own Data Types

A class is a template that defines the structure and behavior for objects. It's like creating your own custom data type with specific properties and actions. Classes help organize related data and functions together.

Creating Your First Class

class Student:
    """A class to represent a student."""
    
    def __init__(self, name, age, grade):
        """Initialize a new student (the constructor)."""
        self.name = name      # Instance attribute
        self.age = age        # Instance attribute
        self.grade = grade    # Instance attribute
        self.courses = []     # Start with empty course list
    
    def add_course(self, course):
        """Add a course to the student's schedule."""
        self.courses.append(course)
        print(f"{self.name} enrolled in {course}")
    
    def get_info(self):
        """Return formatted student information."""
        return f"{self.name}, age {self.age}, grade {self.grade}"

# Creating objects (instances) from the class
student1 = Student("Alice", 16, 10)
student2 = Student("Bob", 17, 11)

# Using methods on objects
print(student1.get_info())  # Alice, age 16, grade 10
student1.add_course("Math")  # Alice enrolled in Math

🎮 Interactive Class Builder

Create your own student and see OOP in action:

Click 'Create Student Object' to begin

🔧 The __init__ Method: Object Constructor

The __init__ method is special - it's automatically called when you create a new object. Think of it as the "setup instructions" that run when a house is built from the blueprint, setting up the initial state.

✅ Constructor in Action

class Car:
    def __init__(self, make, model, year):
        # These run automatically!
        self.make = make
        self.model = model  
        self.year = year
        self.mileage = 0  # Default value

# Creating objects calls __init__
my_car = Car("Toyota", "Camry", 2020)

🔍 What Happens

  • Python creates new object
  • Calls __init__ automatically
  • Sets up instance attributes
  • Returns ready-to-use object

🧠 The 'self' Parameter

self refers to the specific object being created or used. It's like saying "this particular house" when multiple houses exist. Python automatically passes the object as the first argument to all methods.

⚙️ Methods: What Objects Can Do

Methods are functions that belong to a class. They define what actions objects can perform. Think of them as the "abilities" or "behaviors" that every house built from the blueprint can do.

Bank Account Example

class BankAccount:
    def __init__(self, owner, initial_balance=0):
        self.owner = owner
        self.balance = initial_balance
        self.transaction_history = []
    
    def deposit(self, amount):
        """Add money to the account."""
        if amount > 0:
            self.balance += amount
            self.transaction_history.append(f"Deposited ${amount}")
            return f"Deposited ${amount}. New balance: ${self.balance}"
        return "Invalid amount"
    
    def withdraw(self, amount):
        """Remove money from the account."""
        if amount > 0 and amount <= self.balance:
            self.balance -= amount
            self.transaction_history.append(f"Withdrew ${amount}")
            return f"Withdrew ${amount}. New balance: ${self.balance}"
        return "Insufficient funds or invalid amount"
    
    def get_balance(self):
        """Return current balance."""
        return f"{self.owner}'s balance: ${self.balance}"

# Using the BankAccount class
account = BankAccount("Alice", 100)
print(account.deposit(50))    # Deposited $50. New balance: $150
print(account.withdraw(30))   # Withdrew $30. New balance: $120
print(account.get_balance())  # Alice's balance: $120

🎮 Try the Bank Account

Alice's account starts with $100. Try some transactions!

⚠️ Critical Pitfall: Mutable Default Arguments

Dangerous Assumption: Default Lists Are Safe

Beginner trap: Using mutable objects (like lists) as default arguments creates shared state between all instances! All objects will share the same list, leading to unexpected behavior.

❌ Wrong: Mutable Default

class Student:
    def __init__(self, name, courses=[]):  # DANGER!
        self.name = name
        self.courses = courses  # All students share same list!

alice = Student("Alice")
bob = Student("Bob")
alice.courses.append("Math")
print(bob.courses)  # ['Math'] - Bob has Alice's course!

✅ Correct: None Default

class Student:
    def __init__(self, name, courses=None):
        self.name = name
        self.courses = courses if courses else []  # Safe!

alice = Student("Alice")
bob = Student("Bob")
alice.courses.append("Math")
print(bob.courses)  # [] - Bob has his own empty list

🧠 Safe Default Pattern

Always use None as the default for mutable arguments, then create a new object inside the method. This ensures each instance gets its own separate copy.

🎯 Mastery Check: OOP Basics Understanding

Question 1: Classes vs Objects

What is the relationship between a class and an object?

A class is a blueprint/template, an object is an instance created from that class

Classes and objects are the same thing

Objects are blueprints for creating classes

Question 2: The __init__ Method

When is the __init__ method called?

Automatically when a new object is created from the class

Only when you explicitly call it with object.__init__()

When the program starts running

Question 3: Mutable Default Arguments

Why should you avoid using a list as a default parameter in __init__?

All instances would share the same list object, causing unexpected behavior

Lists are not allowed in __init__ methods

It makes the code run slower

🎯 Ready for Advanced OOP!

What You've Mastered

  • ✅ Classes as blueprints and objects as instances
  • ✅ The __init__ constructor method and self parameter
  • ✅ Instance attributes and methods
  • ✅ Creating and using objects
  • ✅ Critical pitfall of mutable default arguments

Now that you understand the basics of classes and objects, let's explore inheritance, polymorphism, and other advanced OOP concepts!

Continue to Advanced OOP →