Constructors and State Initialization with __init__
15 min · Beginner
Need help? Ask a tutor on WhatsApp (opens in a new tab)When you instantiate a class, Python automatically calls the __init__ method. It acts as the initializer where you bind initial values to instance attributes on self.
The __init__ Initializer Method
Initializer (__init__) is the method Python calls automatically right after creating an object. It sets up the starting attributes through self.
__init__ is a dunder (double underscore) method that runs immediately after an object is allocated in memory.
class User: def __init__(self, username, email): self.username = username self.email = email
u = User("alex_dev", "alex@example.com")print(u.username)print(u.email)Example 1 · __init__ runs on its own
class Order: def __init__(self, order_id): print(f"Creating order {order_id}") self.order_id = order_id
o = Order(101)print(o.order_id)Creating order 101 101
What this code does
- You never call
__init__yourself.Order(101)creates the object and then runs it. - The print inside runs during the call, before the next line.
The arguments passed to User(...) are forwarded directly into __init__ after self.
Validating Constructor Inputs
Input validation means checking values when an object is created and raising an error for bad ones, so no object ever holds invalid data.
A robust class validates inputs at creation time so an object can never exist in an invalid state.
class Account: def __init__(self, owner, balance=0.0): if balance < 0: raise ValueError("Initial balance cannot be negative") self.owner = owner self.balance = float(balance)Example 2 · Rejecting bad data
class Account: def __init__(self, owner, balance=0.0): if balance < 0: raise ValueError("Initial balance cannot be negative") self.owner = owner self.balance = float(balance)
try: Account("Ravi", -50)except ValueError as err: print(err)
ok = Account("Ravi", 200)print(ok.balance)Initial balance cannot be negative 200.0
What this code does
- The
raisestops__init__, so no bad account is ever created. - A valid call completes, and
float()turns 200 into 200.0.
Raising a ValueError prevents corrupted state from circulating through your backend.
Default Parameters in __init__
You can provide default values for optional attributes.
class Server: def __init__(self, host="localhost", port=8000): self.host = host self.port = portExample 3 · Defaults for optional values
class Product: def __init__(self, name, price, stock=10): self.name = name self.price = price self.stock = stock
p = Product("Mechanical Keyboard", 4500)print(f"{p.name} - Rs. {p.price} | Stock: {p.stock}")Mechanical Keyboard - Rs. 4500 | Stock: 10
What this code does
p.nameandp.pricereceive the passed arguments.p.stockfalls back to the default parameter 10.
Example 4 · Defaults and keyword arguments
class Server: def __init__(self, host="localhost", port=8000): self.host = host self.port = port
s1 = Server()s2 = Server(port=9000)print(s1.host, s1.port)print(s2.host, s2.port)localhost 8000 localhost 9000
What this code does
- Skipped arguments take their defaults.
port=9000names the argument, sohoststill uses its default.
Callers can instantiate Server() with defaults or override with Server(port=9000).
The Mutable Default Argument Danger
Mutable default argument is a list or dictionary written as a parameter default. It is built once, so every call that uses the default shares it.
Never use a mutable object (like [] or {}) as a default parameter in __init__. The default is evaluated once when the function is defined, sharing the list across all instances!
# BUGGY:# def __init__(self, items=[]):
# CORRECT:class Cart: def __init__(self, items=None): self.items = items if items is not None else []Example 5 · Shared default list versus a safe one
class BadCart: def __init__(self, items=[]): self.items = items
class Cart: def __init__(self, items=None): self.items = items if items is not None else []
x = BadCart()y = BadCart()x.items.append("Pen")print(y.items)
a = Cart()b = Cart()a.items.append("Pen")print(b.items)['Pen'] []
What this code does
- The default
[]inBadCartis created once, soxandyhold the same list. Cartcreates a new list on each call whenitemsisNone.
Using None as the sentinel value ensures every instance gets its own independent list.
Next step
Check your understanding
11 quick questions