Topic 8.1
Classes and Objects
In one line
A class is a blueprint that describes what a kind of thing knows (its attributes) and what it can do (its methods); an object, also called an instance, is one real thing built from that blueprint. __init__ sets up each new object, and self is the name a method uses for the object it was called on.
Think of it like this
A cookie cutter and cookies. The cutter is the shape; it is not a cookie. Press it into dough ten times and you get ten separate cookies with the same shape. You can decorate one cookie with sprinkles and the others stay plain. A class is the cutter. Each object (also called an instance) is one cookie made from it.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Class
- A blueprint that describes a kind of object: its data and its behaviour.
- Object (instance)
- One real thing built from a class.
Dog("Rex", 3)makes one Dog instance. - Attribute
- A named piece of data attached to an object, such as
rex.name. - Method
- A function defined inside a class, called on an object, such as
rex.speak(). - `__init__`
- The initialiser: Python calls it right after creating a new object, so it can set the starting attributes.
- `self`
- The first parameter of a method. It is the object the method was called on.
- Dunder
- A name with double underscores on both sides, like
__init__. Python calls these for you. - Instantiate
- To create an object from a class by calling the class:
Dog("Rex", 3). - OOP
- Object-oriented programming: organising a program as objects that hold data and behaviour together.
Step by step
01Why a class at all
Suppose you track dogs with plain variables or dictionaries. A dictionary {"name": "Rex", "age": 3} holds the data, but nothing stops you writing "nmae" by mistake, and the code that works on dogs is scattered in separate functions.
A class gathers the data and the functions that use it into one named thing. That gives you one place to read, one place to fix and a real type that tools and teammates can recognise.
02Write the class and make objects
class Dog: starts the blueprint. Inside, def __init__(self, name, age) sets the starting attributes. Notice that you call Dog("Rex", 3) with two arguments: Python supplies self for you.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
return self.name + " says Woof!"
rex = Dog("Rex", 3)
bella = Dog("Bella", 5)
print(rex.speak())03What Dog(...) does, step by step
Calling a class runs three steps. First Python creates a new, empty object. Second it calls Dog.__init__(that_object, "Rex", 3), which fills in the attributes. Third it hands the finished object back, and rex is a name for it.
Each object has its own small dictionary of attributes, while the methods live once on the class.
04self is the object before the dot
rex.speak() is shorthand for Dog.speak(rex). Python looks up speak on the class and passes the object as the first argument. That is why def speak(self) has one parameter even though you call it with none.
If you leave self out of the definition, the call fails with a TypeError: Python still passes the object, and the method has nowhere to put it.
rex.speak() # what you write
Dog.speak(rex) # what Python runs: same result05Methods change the object they belong to
A method can change self's attributes. rex.birthday() adds one to Rex's age only. Bella's age is untouched, because each object owns its attributes.
A method can also return self, which lets you chain calls: counter.add().add().add(). Many libraries use this style.
def birthday(self):
self.age = self.age + 106Everything is an object
type() tells you which class an object was built from. 42 is an int, a function is a function, and a class is a type. Even (5).bit_length() works because the number 5 is an object with methods.
This is why Python feels uniform: you can pass a class to a function, store a function in a list or print an attribute of a number. The next topics build on this.
07Printing an object needs __repr__
If you print an object of your own class without any extra code, Python shows something like <__main__.Dog object at 0x7f3a...>. The part after at is a memory address, which tells you nothing and changes on every run.
Define __repr__ to return a helpful string, ideally one that looks like the code that makes the object. Topic 8.7 covers it fully; this topic uses it early so you can see your objects.
def __repr__(self):
return "Dog(name=" + repr(self.name) + ", age=" + str(self.age) + ")"08Habits that save you later
Name classes in CapWords (BankAccount) and everything else in snake_case. Set every attribute inside __init__, even if the starting value is None, so a reader sees the full shape of the object in one place.
Not everything needs a class. If you only need to group three values, a dictionary or (Topic 8.8) a dataclass is shorter. Use a class when data and behaviour belong together.
Try it yourself
- 1
Make a third dog
In the first example, add
max_dog = Dog("Max", 1)and printmax_dog.speak(). Before running, predict what it prints. Then callmax_dog.birthday()twice and print its age. - 2
Add your own method
Add a method
is_puppy(self)that returnsTruewhenself.age < 2. Call it on each dog. A method is only a function that takesselffirst. - 3
Call a method the long way
In the second example the call
m(c2)usesCounter.adddirectly. TryCounter.add(c)on the first counter and printc.count. You are doing by hand whatc.add()does for you.
Code & diagrams
Press Run. Then add a third dog and call its methods. Notice that Rex's birthday does not change Bella.
Expected output
Rex says Woof!
Bella says Woof!
Rex is now 4
Bella is still 5
Dog(name='Rex', age=4)
<class '__main__.Dog'>
FalseThe loop prints the first 20 characters of each repr, so the output stays the same on every run. Try adding more values to the list.
Expected output
count: 3
42 -> int
'hi' -> str
[1, 2] -> list
<built-in function l -> builtin_function_or_method
<class '__main__.Cou -> type
<__main__.Counter ob -> Counter
classes are objects too: <class 'type'>
int is a class: True
a function has attributes: len
(5).bit_length() = 3
count via Counter.add(c2): 2
extra ['count', 'label']The address after at differs on every run, which is why runnable examples on this site define __repr__ before printing an object. The address shown here is only an example.
class Plain:
pass
p = Plain()
print(p)Output
<__main__.Plain object at 0x7f0c3a1b2d50>Dogs are a toy. This is closer to real programs: each customer has their own cart (data) and the cart knows how to add items and total the bill (behaviour). Each cart keeps its own items list. Add a third customer and a method that counts items.
Expected output
ShoppingCart('Asha', 2 items) total: 75
ShoppingCart('Ben', 1 items) total: 500
{'name': 'pen', 'price': 10, 'quantity': 3}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget self in a method
Define def speak(): with no parameter and call rex.speak().
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def speak():
return "Woof"
rex = Dog("Rex", 3)
rex.speak()Break #2
Create an object with a missing argument
Call Dog("Rex") when __init__ needs both a name and an age.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
rex = Dog("Rex")Break #3
Read an attribute that was never set
Print rex.age when __init__ only sets name.
class Dog:
def __init__(self, name):
self.name = name
rex = Dog("Rex")
print(rex.age)Myth vs fact
Myth
A class and an object are the same thing.
Fact
A class is the blueprint, and an object is one thing built from it. You write one Dog class and can create thousands of Dog objects, each with its own data.
Myth
self is a special Python keyword.
Fact
self is only the conventional name of the first parameter. Python would accept any name. Everyone uses self, so code with another name is hard to read.
Myth
Methods are copied into every object.
Fact
Methods are stored once, on the class. Each object keeps only its own attributes, and Python looks up the method on the class when you call it.
Myth
You must use classes to write real Python.
Fact
Plenty of good Python is plain functions and data. Classes are one tool, best when data and behaviour belong together. Topic 8.11 covers when not to use one.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Under the hood
Dog("Rex", 3)callstype.__call__(Dog, "Rex", 3), which callsDog.__new__to allocate the object and thenDog.__init__to fill it. You rarely write__new__; it matters for immutable types and singletons. - ▸
By default each instance stores its attributes in a dictionary called
__dict__. That is why you can add attributes freely, and also why plain objects use more memory than a tuple. Topic 8.12 shows how__slots__removes it. - ▸
A bound method such as
rex.speakis itself an object that remembersrex.rex.speak.__self__isrexandrex.speak.__func__is the plain function on the class. This is the mechanism behindself. - ▸
In AI code, small classes often wrap an API client or hold conversation state: one object per conversation, with a list of messages and methods like
add_user_message. For pure data, a dataclass (Topic 8.8) is usually nicer than writing__init__by hand.
Remember this
- 1
You have used objects since Phase 1:
"hi"is astrobject,[1, 2]is alistobject, andlenis a function object. A class is how you add your own kinds. You writeclass Dog:followed by an indented block, and Python creates a new type calledDog. Calling it like a function,Dog("Rex", 3), builds a newDogobject. - 2
An object holds data in attributes: names attached to the object, written
rex.name. It also has behaviour in methods: functions defined inside the class, called asrex.speak(). Data and behaviour that belong together live together, which is the whole idea of object-oriented programming (OOP). - 3
__init__is the initialiser. Python calls it automatically right after it creates an empty object, passing the arguments you gave toDog(...). Inside, you store the starting data:self.name = name. Its name has two underscores on each side, a pattern called a dunder (double underscore) that marks a method Python itself knows how to call.__init__returns nothing; it only fills in the object. - 4
selfis the first parameter of every normal method, and it is the object the method was called on. When you writerex.speak(), Python runsDog.speak(rex), soselfisrex. The nameselfis only a convention, but every Python programmer uses it, and so should you. This is why a method can sayself.age + 1and get that dog's age, not another dog's. - 5
Two objects of the same class are independent. Changing
rex.agedoes not touchbella.age, because each object has its own set of attributes. They share only the class: the methods are stored once on the class and every instance uses them.rex is bellaisFalseeven if both have the same data, because they are different objects. - 6
In Python, everything is an object, including numbers, functions and the class itself.
type(rex)isDog, andtype(Dog)istype: classes are objects too, built bytype. Python is also flexible: you can add a new attribute to an existing object at any time (rex.owner = "Sam"). That flexibility is useful but makes typos silent, so set all attributes in__init__.
Explain it without notes
What is the difference between a class and an object?
What does __init__ do, and when is it called?
What is self, and why do methods take it explicitly?
What does it mean that "everything is an object" in Python?
Practice
Write a class Rectangle with attributes width and height, and methods area() and perimeter(). Print both for a 3 by 4 rectangle.
Write a class Counter with a count starting at 0, an add() method that increases it and returns self, and a reset() method. Add three times in a chain and print the count.
Write a class Book with title and pages, and a __repr__ that returns text like Book('Python', 300). Print a list of two books.
Trade-offs
- ↔
A class bundles data and behaviour, at the cost of more code. For two or three values with no behaviour, a dictionary or dataclass is shorter. Reach for a class when methods need to keep the data consistent.
- ↔
Free attribute creation is flexible but error-prone. You can add
rex.ownerany time, and you can also mistyperex.agee. Setting every attribute in__init__and using dataclasses or__slots__(Topics 8.8 and 8.12) trades some flexibility for safety.
Done when you can
Done when you can explain the difference between a class and an object with an example.
Done when you can write a class with
__init__, attributes and two methods.Done when you can explain why
rex.speak()is the same asDog.speak(rex).Done when you can explain why two objects from the same class do not share attributes.
Done when you can say why printing a plain object shows a memory address, and how
__repr__fixes it.