Phase 3 · Object-Oriented PythonModule 13~40 min read

Object-Oriented Programming Fundamentals

Model the world with classes, objects, __init__, and self.

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 understand self
  • 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 vs object

class — the blueprint

Dog

Defines what every dog has and does.

→
Rex (object)
Buddy (object)
Max (object)
dog.py
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:

attributes.py
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:

point.py
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

If you only write one, make it __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.