OOP introduction

Classes and objects

Every value you've used so far — strings, lists, dicts — is an object: a bundle of data plus the functions (called methods when they belong to an object) that operate on it. "hello".upper() is calling a method that belongs to the string object "hello".

A class is a blueprint for creating your own objects, with your own data and your own methods.

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

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

init and self

__init__ is a special method Python calls automatically when you create a new object — it's where you set up the object's initial data (its attributes).

self is the object itself. Every method on a class takes self as its first parameter, and Python passes it automatically — you never pass it yourself when calling a method. Inside a method, self.name accesses (or sets) the name attribute on this particular object.

Creating and using objects

rex = Dog("Rex", 3)     # calls __init__(self=rex, name="Rex", age=3)
rex.name                 # "Rex"
rex.bark()                # "Rex says woof!"

fido = Dog("Fido", 5)
fido.name                 # "Fido" -- a completely separate object from rex

Each object created from a class has its own copy of the attributes set in __init__rex.name and fido.name don't interfere with each other, even though they came from the exact same class.

Why bother?

You could represent a dog as a dict ({"name": "Rex", "age": 3}) and a separate bark(dog) function. A class bundles the data and the behavior that operates on it into one thing, under one name — as your programs grow, that bundling makes it much easier to reason about "what can a Dog do" (look at the class) instead of hunting down every loose function that happens to take a dog-shaped dict.