Classes and Objects
Bundle data and behaviour into your own types
In this lesson
In Python, a class is a blueprint for your own kind of thing, and an object is one specific thing built from that blueprint — bundling related data together with the actions that belong to it. You already group values with dictionaries and reuse logic with functions; a class brings those two ideas together. By the end you’ll define a class, make objects from it, and see why that’s often clearer than loose dictionaries.
Explain it like I’m 5
A class is a recipe; an object is the cookie you bake from it. You write the recipe once, then bake as many cookies as you like — and each cookie is its own separate cookie.
From a loose dictionary to a class
Suppose you’re tracking pets. With a dictionary you’d write one dict per pet and reach in by key. That works, but nothing stops a typo like pet["naem"], and any behaviour — say, ‘describe this pet’ — lives in separate functions far from the data.
A class is a blueprint for a new type. It says what every pet has (its data) and what every pet can do (its methods), all in one place. From that one blueprint you create objects (also called instances): individual pets, each with its own values.
# One pet as a dictionary
pet = {"name": "Rex", "species": "dog", "age": 3}
print(pet["name"], "is a", pet["age"], "year old", pet["species"])
Rex is a 3 year old dog
class Pet:
def __init__(self, name, species, age):
self.name = name
self.species = species
self.age = age
rex = Pet("Rex", "dog", 3)
print(rex.name, "is a", rex.age, "year old", rex.species)
Rex is a 3 year old dog
__init__ and self, decoded
Two bits of class syntax trip up newcomers. __init__ is the setup method Python runs automatically every time you create an object — it’s where the new object gets its starting attributes. self simply means ‘this particular object’: when you write self.name = name, you’re saying ‘store this name on the object being built right now’.
You never pass self yourself — Python fills it in. Pet("Rex", "dog", 3) calls __init__ with self set to the brand-new pet and the three values lined up after it.
Methods: behaviour that lives with the data
A method is just a function defined inside a class. Because its first parameter is self, it can read and change that object’s attributes. This is the real payoff of classes: the action that belongs to a pet lives right next to the pet’s data.
class Pet:
def __init__(self, name, age):
self.name = name
self.age = age
def describe(self):
return f"{self.name} is {self.age} years old"
rex = Pet("Rex", 3)
print(rex.describe())
Rex is 3 years old
Add a birthday() method that adds one year to the pet's age. Call it once, then print the pet's age.
class Pet:
def __init__(self, name, age):
self.name = name
self.age = age
def birthday(self):
pass # TODO: add one year to self.age
rex = Pet("Rex", 3)
rex.birthday()
print(rex.age)
Inside the method, change the attribute on self: self.age += 1.
class Pet:
def __init__(self, name, age):
self.name = name
self.age = age
def birthday(self):
self.age += 1
rex = Pet("Rex", 3)
rex.birthday()
print(rex.age)
4
Common mistake: Forgetting self in a method definition
Methods look like ordinary functions, so it’s easy to write def describe(): and leave out the self parameter.
Every regular method needs self as its first parameter. Without it, calling rex.describe() raises a TypeError about an unexpected argument, because Python still passes the object in.
Common mistake: Calling a method without parentheses
rex.describe looks like it should run, but without () you get the method object itself, not its result.
Add the parentheses: rex.describe(). The empty parentheses are how you actually call it.
Common mistake: Confusing the class with the object
The class and the things made from it feel like the same idea at first.
The class (Pet) is the blueprint; the object (rex) is one thing built from it. You can make many objects from one class, and each keeps its own attribute values.
Which line creates an object from the Pet class?
Calling the class like a function — Pet(...) — builds a new object and runs __init__.
Inside a method, what does self.name refer to?
self is the particular object, so self.name is that object’s own name attribute.
Why can two pets made from the same class have different names?
Each call to Pet(...) makes a separate object with its own attributes, so they don’t share values.
Mini exercise (medium)
Define a Pet class with name, species, and age attributes and a describe() method that returns a sentence like "Rex is a 3-year-old dog". Create two pets and print each one’s description.
Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.
class Pet:
def __init__(self, name, species, age):
# TODO: store name, species and age on self
pass
def describe(self):
# TODO: return e.g. "Rex is a 3-year-old dog"
return ""
rex = Pet("Rex", "dog", 3)
mia = Pet("Mia", "cat", 2)
print(rex.describe())
print(mia.describe())
Store the three values on self in __init__, then build the sentence in describe() with an f-string.
class Pet:
def __init__(self, name, species, age):
self.name = name
self.species = species
self.age = age
def describe(self):
return f"{self.name} is a {self.age}-year-old {self.species}"
rex = Pet("Rex", "dog", 3)
mia = Pet("Mia", "cat", 2)
print(rex.describe())
print(mia.describe())
Rex is a 3-year-old dog
Mia is a 2-year-old cat
assert rex.name == "Rex" and rex.species == "dog" and rex.age == 3, "store name, species and age on self in __init__"
assert rex.describe() == "Rex is a 3-year-old dog", "describe() should read self's attributes"
assert mia.describe() == "Mia is a 2-year-old cat", "each object should keep its own values"
print("✓ Two pets, one class!")