Object-Oriented ProgrammingIntermediate7 min40 / 63

The __init__ Constructor

Learn how __init__ automatically sets up a new object the moment it is created, and why that makes your classes so much more powerful.

Imagine ordering a coffee. The moment you place the order, the barista already knows: what size, what flavour, your name for the cup. You did not have to hand those details over one by one after the cup appeared — they came built-in from the start.

That is exactly what __init__ does for Python objects. It is a special method that runs automatically the instant you create a new instance of a class. By the time you have a shiny new object in your hands, all its starting details are already set.

See it in action

Visual walkthrough1 / 5
1

Objects That Arrive Ready

Every time you create an object, Python can automatically run setup code for you. That magic happens inside a special method called __init__.

"init" is short for initialise — it sets up the object the moment it is born.

#Classes vs Instances — a quick reminder

Think of it like

Blueprint vs Building

A class is like an architect's blueprint. It describes what every building of that type will look like. An instance is one actual building constructed from that blueprint. You can build many buildings from one blueprint, and each building can have its own address, colour, and number of floors.

Two instances from the same class — both are Dogs, but they live at different places in memory.
class Dog:
    pass

rex = Dog()   # rex is one instance
bella = Dog() # bella is a different instance

print(rex)   # different objects in memory
print(bella)

Right now Dog is empty. Every dog is identical and anonymous. Let's fix that with __init__.

#Writing Your First __init__

Python calls __init__ automatically. You pass the arguments after the class name.
class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

rex = Dog("Rex", "Labrador")
print(rex.name)
print(rex.breed)

Let's break down what happened line by line:

  • def __init__(self, name, breed): — defines the setup method. It always receives self as its first argument.
  • self.name = name — stores the value of name on the object itself. Think of it as pinning a name tag to that specific dog.
  • Dog("Rex", "Labrador") — Python creates a new Dog and immediately calls __init__ with "Rex" and "Labrador". You never see that call — Python makes it for you.

#What is 'self'?

Think of it like

Self = 'this very object'

self is just a name (you could technically use any word, but never do — always use self). It refers to the specific instance being set up right now. When Python runs rex = Dog("Rex", "Labrador"), it passes rex as self automatically. So self.name = name really means rex.name = "Rex".

#Each Instance Has Its Own Attributes

rex and bella are separate objects. Changing one does not affect the other.
class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

rex = Dog("Rex", "Labrador")
bella = Dog("Bella", "Poodle")

print(rex.name, rex.breed)
print(bella.name, bella.breed)

#Default Parameter Values

Sometimes you want a sensible default so callers do not have to provide every value. You can give __init__ parameters default values just like any other Python function.

If you skip 'breed', Python uses the default value 'Unknown'.
class Dog:
    def __init__(self, name, breed="Unknown"):
        self.name = name
        self.breed = breed

dog1 = Dog("Max", "Beagle")  # breed provided
dog2 = Dog("Buddy")          # breed uses default

print(dog1.name, dog1.breed)
print(dog2.name, dog2.breed)
Tip

Put defaults at the end

Parameters with defaults must always come after parameters without defaults. def __init__(self, name, breed="Unknown") is fine. def __init__(self, breed="Unknown", name) would raise a SyntaxError.

#You Never Call __init__ Directly

Common mistake

Don't call __init__ yourself

You should never write rex.__init__("Rex", "Labrador") in normal code. Python calls it for you when you write Dog("Rex", "Labrador"). Calling it manually would run the setup code a second time on an already-created object, which is almost always a bug.

#A Richer Example

__init__ sets up owner and balance; deposit uses self to access them later.
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount
        print(f"{self.owner} deposited ${amount}. Balance: ${self.balance}")

account = BankAccount("Alice", 100)
account.deposit(50)

Notice that deposit can read self.owner and self.balance because __init__ stored them on the object. Every method in the class can access attributes set in __init__ through self.

#Computed Attributes

Not every attribute has to come directly from a parameter. You can compute values inside __init__ too.

self.area is calculated once during __init__ and stored on the object.
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height  # computed at creation time

r = Rectangle(5, 3)
print(r.area)
Quick check

What does Python do when you write `dog = Dog("Rex", "Labrador")`?

Key takeaways

  • `__init__` runs automatically every time you create a new instance — you never call it yourself.
  • Use `self.attribute = value` inside `__init__` to store data on the specific object being created.
  • Arguments you pass at `Dog("Rex")` flow directly into `__init__`'s parameters (after `self`).
  • Default parameter values let callers skip optional arguments: `def __init__(self, name, breed="Unknown")`.
  • A class is the blueprint; an instance is a real object built from it — each instance has its own independent attributes.
Practice challenges
Test yourself · earn XP
0/4
Predict the output#1

What does this code print?

predict-output
class Dog:
    def __init__(self, name, breed="Unknown"):
        self.name = name
        self.breed = breed

dog1 = Dog("Rex", "Labrador")
dog2 = Dog("Buddy")

print(dog1.name, dog1.breed)
print(dog2.name, dog2.breed)
Fix the bug#2

This code has a bug. What is wrong?

fix-bug
class Rectangle:
    def __init__(self, width, height):
        width = width
        height = height
        self.area = width * height

r = Rectangle(4, 5)
print(r.area)
print(r.width)
Fill in the blank#3

Complete the __init__ method so that a BankAccount stores the owner's name and a starting balance, then prints correctly.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = 
        self.balance = 

acc = BankAccount("Alice", 100)
print(acc.owner)
print(acc.balance)
Reorder the lines#4

Put these lines in the right order to define a Movie class and print the title of a new instance.

1
m = Movie("Inception", 2010)
2
        self.title = title
3
print(m.title)
4
class Movie:
5
        self.year = year
6
    def __init__(self, title, year):
Your turn
Practice exercise

Create a class called Movie with an __init__ method that accepts title, director, and year. Give year a default value of 2024. Then create two Movie instances: one with all three arguments, and one using only title and director. Print the title and year of each.

Try it live — edit the code and hit Run to execute real Python:

solution.py · editable