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