What you'll learn
This is where Python really opens up. Object-oriented programming (OOP) lets you model the world as objects that bundle data and behaviour together. You define your own types with classes — the foundation of almost every serious Python program.
By the end you'll be able to:
- Define classes and create objects
- Initialise objects with
__init__and understandself - Distinguish class attributes from instance attributes
- Give your objects readable output with
__str__and__repr__
Classes & objects
A class is a blueprint; an object (or instance) is a concrete thing built from it. The class defines attributes (data) and methods (behaviour):
class — the blueprint
Dog
Defines what every dog has and does.
class Dog:
def __init__(self, name, age): # the constructor
self.name = name # instance attributes
self.age = age
def bark(self): # a method
print(f"{self.name} says Woof!")
rex = Dog("Rex", 3) # create an object
rex.bark()
print(rex.name, "is", rex.age)__init__ & self
__init__ is a special method (a "dunder" — double-underscore) that runs automatically when you create an object. It sets up the initial state. The first parameter of every method is self — a reference to the current object. You never pass self yourself; Python supplies it, and you use it to store and read the object's data (self.name).
Note
self is just a naming convention (a strong one — always use it). When you call rex.bark(), Python calls Dog.bark(rex) under the hood, passing rex as self.Class vs instance attributes
An instance attribute (set with self.x in __init__) is unique to each object. A class attribute (defined directly in the class body) is shared by every instance — useful for constants and defaults:
class Dog:
species = "Canis familiaris" # class attribute — shared by ALL dogs
def __init__(self, name):
self.name = name # instance attribute — unique per dog
rex = Dog("Rex")
buddy = Dog("Buddy")
print(rex.name, buddy.name) # Rex Buddy (different)
print(rex.species) # shared
print(Dog.species) # access on the class too__str__ & __repr__
By default, printing an object shows something unhelpful like <__main__.Point object at 0x...>. Define __str__ (a readable description for users) and __repr__ (an unambiguous one for developers, ideally showing how to recreate the object) to fix that:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self): # for developers (unambiguous)
return f"Point(x={self.x}, y={self.y})"
def __str__(self): # for users (readable)
return f"({self.x}, {self.y})"
p = Point(3, 4)
print(p) # uses __str__ -> (3, 4)
print(repr(p)) # uses __repr__ -> Point(x=3, y=4)Tip
__repr__ — Python falls back to it for str() too, and it's what you see in the debugger and REPL.Recap & quick check
Key takeaways
- A class is a blueprint; an object is an instance created from it.
- __init__ runs on creation to set up state; self refers to the current object.
- Instance attributes (self.x) are per-object; class attributes are shared by all instances.
- __str__ gives a readable string for users; __repr__ an unambiguous one for developers.
- Methods always take self as their first parameter.
Quick check
1. What does __init__ do?
2. What is 'self'?
3. A class attribute is…
4. Which method gives a readable string when you print an object?
5. How do you create an object of class Dog?
Brilliant — you've entered object-oriented thinking. Next up: Module 14 — Encapsulation, Properties & Class Methods.