chapter 9.1

Intro to OOP

Why objects? Classes, instances, __init__, and self

Up to now, you've organized code into variables and functions. That works great for small programs, but as things grow, you need a way to bundle related data and behavior together. That's what Object-Oriented Programming (OOP) does.
A class is a blueprint. An instance is a thing built from that blueprint. The class Dog describes what a dog *is* — the instance my_dog is a *specific* dog.
class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    def bark(self):
        print(f"{self.name} says: Woof!")

# Create instances
rex = Dog("Rex", "German Shepherd")
buddy = Dog("Buddy", "Golden Retriever")

print(rex.name)    # Rex
print(buddy.breed) # Golden Retriever
rex.bark()         # Rex says: Woof!
Let's break down what just happened:
◇info
class Dog: — defines a new class (blueprint)
__init__(self, name, breed) — the initializer. Runs automatically when you create an instance. Sets up the object's data.
self — refers to the specific instance being created or used. Python passes it automatically — you just have to include it as the first parameter.
self.name = name — stores name as an attribute on the instance
Why self? When you call rex.bark(), Python actually runs Dog.bark(rex) under the hood — it passes the instance as the first argument. That's why every method needs self as its first parameter.
# These two lines do the same thing:
rex.bark()        # Python passes rex as self
Dog.bark(rex)     # Explicitly passing rex as self
Here's a more practical example — a BankAccount:
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.balance += amount
            print(f"Deposited ${amount}. Balance: ${self.balance}")

    def withdraw(self, amount):
        if amount > self.balance:
            print("Insufficient funds!")
        else:
            self.balance -= amount
            print(f"Withdrew ${amount}. Balance: ${self.balance}")

account = BankAccount("Ada", 100)
account.deposit(50)     # Deposited $50. Balance: $150
account.withdraw(200)   # Insufficient funds!
account.withdraw(75)    # Withdrew $75. Balance: $75
Each instance has its own data — changing one doesn't affect the other:
a = BankAccount("Alice", 500)
b = BankAccount("Bob", 100)

a.deposit(50)
print(b.balance)  # Still 100 — separate instances
your turn
challenge

Create a `Student` class with: - `__init__` that takes `name` and `grade`, and initializes an empty `courses` list - An `enroll(course)` method that adds a course to the list - An `info()` method that prints the student's name, grade, and enrolled courses

loading editor...

output
$ Press RUN to execute your code
AI Tutor

Ask about Intro to OOP

◇

Hey! I'm your AI tutor.

Ask me anything about this lesson, or paste code you're stuck on.

AI tutor is a premium feature