The __init__ Method
__init__() is called immediately after an object is created. The first parameter self refers to the newly created instance.
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) # 5Understanding 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.
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.0Default Constructor Arguments
Use default values for optional attributes.
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)Validation in __init__
Validate arguments inside __init__ to prevent invalid objects from being created.
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}")Class Attributes vs Instance Attributes
Class attributes are shared across all instances. Instance attributes are unique per object.
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__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 attribute | Class attribute | |
|---|---|---|
| Defined | self.x = in __init__ | directly in the class body |
| Shared? | no — one per object | yes — 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:
- Define
__init__that acceptsownerand an optionalbalancedefaulting to 0. - Store both as instance attributes using
self. - Raise a
ValueErrorin the constructor if the starting balance is negative. - 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. selfrefers 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
selfand 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?
Related Topics
FAQ
__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.
__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.
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.
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.

