Advertisement
🏗️ OOP

Python Constructors – __init__ and Object Initialisation

A constructor is special method that runs automatically when an object is created. In Python, the constructor is __init__(). It sets up the initial state of an object by assigning values to instance attributes. Understanding constructors is fundamental to working with classes.

⏱️ 18 min read🎯 Intermediate📅 Updated 2026

The __init__ Method

__init__() is called immediately after an object is created. The first parameter self refers to the newly created instance.

Python
class Dog:
    def __init__(self, name, breed, age):
        self.name = name    # Instance attribute
        self.breed = breed
        self.age = age
        self.tricks = []    # Default empty list

# Creating objects — __init__ runs automatically
rex = Dog("Rex", "German Shepherd", 3)
buddy = Dog("Buddy", "Labrador", 5)

print(rex.name)   # Rex
print(buddy.age)  # 5
▶ Output
Rex 5

Understanding self

self is a reference to the instance being created. It is not a keyword — you could name it anything — but self is the universal Python convention.

Python
class Circle:
    def __init__(self, radius):
        self.radius = radius          # "this circle's radius"
        self.area = 3.14 * radius**2  # Computed on creation

c1 = Circle(5)
c2 = Circle(10)

print(c1.radius, c1.area)   # 5 78.5
print(c2.radius, c2.area)   # 10 314.0
▶ Output
5 78.5 10 314.0

Default Constructor Arguments

Use default values for optional attributes.

Python
class User:
    def __init__(self, username, email, role="user", active=True):
        self.username = username
        self.email = email
        self.role = role
        self.active = active

admin = User("alice", "alice@example.com", role="admin")
guest = User("bob", "bob@example.com")

print(admin.role)   # admin
print(guest.role)   # user (default)
▶ Output
admin user
Advertisement

Validation in __init__

Validate arguments inside __init__ to prevent invalid objects from being created.

Python
class BankAccount:
    def __init__(self, owner, balance=0):
        if not owner:
            raise ValueError("Owner name required")
        if balance < 0:
            raise ValueError("Balance cannot be negative")
        self.owner = owner
        self.balance = balance

try:
    bad = BankAccount("", -100)
except ValueError as e:
    print(f"Error: {e}")

good = BankAccount("Alice", 1000)
print(f"{good.owner}: ${good.balance}")
▶ Output
Error: Owner name required Alice: $1000

Class Attributes vs Instance Attributes

Class attributes are shared across all instances. Instance attributes are unique per object.

Python
class Employee:
    company = "Algorid"   # Class attribute
    employee_count = 0

    def __init__(self, name, salary):
        self.name = name          # Instance attribute
        self.salary = salary
        Employee.employee_count += 1  # Modify class attr

e1 = Employee("Alice", 75000)
e2 = Employee("Bob", 80000)

print(e1.company)          # Algorid (class attr)
print(Employee.employee_count)  # 2
▶ Output
Algorid 2

__init__: The Constructor That Sets Up Each Instance

__init__ runs automatically right after a new object is created, and its job is to set up that instance's starting state. self is the instance being built — every instance attribute hangs off it.

class Account:
    bank = "Acme"                      # class attribute — SHARED by all instances

    def __init__(self, owner, balance=0):
        self.owner = owner             # instance attribute — unique per object
        self.balance = balance

a = Account("Ann", 100)                # __init__ runs with self=a
b = Account("Bob")                     # balance defaults to 0
Instance attributeClass attribute
Definedself.x = in __init__directly in the class body
Shared?no — one per objectyes — one for all

self explained: it's the first parameter of every method and refers to "this particular object." Python passes it automatically when you call a.method(). Instance vs class attributes: self.balance is unique to each account; bank defined in the class body is shared by all. The mutable class-attribute trap: never write class C: items = [] as a default — that list is shared across every instance. Create per-instance mutable state inside __init__ (self.items = []) instead. __init__ is technically an initializer (the object already exists); the actual creation is __new__, which you rarely touch.

🏋️ Practical Exercise

Create a BankAccount class:

  1. Define __init__ that accepts owner and an optional balance defaulting to 0.
  2. Store both as instance attributes using self.
  3. Raise a ValueError in the constructor if the starting balance is negative.
  4. Create two accounts and confirm they hold independent data.

🔥 Challenge Exercise

Build a Product class that tracks a class attribute count of how many products have been created. In __init__, accept name and price, validate that price is positive, assign a unique id based on the running count, and increment the class counter. Create several products and print each one’s id to confirm the counter is shared across all instances.

📋 Summary

  • The __init__ method initializes a new object’s attributes and runs automatically when you create an instance.
  • self refers to the instance being created and must be the first parameter of instance methods.
  • Constructor parameters can have default values, making arguments optional.
  • Validation logic (e.g. raising ValueError) belongs in __init__ to reject invalid objects early.
  • Instance attributes (self.x) are per-object; class attributes are shared across all instances.
  • Python has no method overloading — only the last __init__ defined takes effect; use default arguments instead.

Interview Questions on Constructors

  • What is the purpose of the __init__ method in Python?
  • Is __init__ the actual constructor? How does it differ from __new__?
  • What is self and why is it the first parameter of instance methods?
  • How do you give constructor parameters default values?
  • What is the difference between a class attribute and an instance attribute?
  • Can a Python class have more than one __init__ method?
  • How do you validate arguments passed to a constructor?

FAQ

Is __init__ really the constructor? +

Not exactly. __new__ actually creates and returns the new instance; __init__ then initializes it. For everyday classes you only override __init__, which is why it is commonly called the constructor.

What happens if I don’t define __init__? +

Python supplies a default constructor that takes no extra arguments and creates an empty object. You can still add attributes later, but defining __init__ lets you require and validate initial data.

Why do I have to write self everywhere? +

Python passes the instance explicitly as the first argument to instance methods. Naming it self is convention. It lets each method know which object’s data it is working with.

Can I call one constructor from another? +

A subclass constructor can call its parent’s with super().__init__(...). For alternative constructors on the same class, use @classmethod factory methods that build and return an instance.