Table of Contents
What `__init__` Does in a Class
In Python, __init__ is a special method that runs automatically every time you create a new object from a class.
You’ll usually use it to:
- Receive values (arguments) when creating an object
- Store those values in attributes on the object
- Set up any default values the object should start with
__init__ is not the only method a class can have, but it is the one that controls how a new object is initialized.
Basic pattern:
class ClassName:
def __init__(self, parameters_here):
# set up the new object
# create attributes
passA First Example of `__init__`
Here’s a simple class with an __init__ method:
class Person:
def __init__(self, name, age):
# create attributes on this new object
self.name = name
self.age = age
# creating objects
p1 = Person("Alice", 30)
p2 = Person("Bob", 25)
print(p1.name) # Alice
print(p2.age) # 25Key ideas:
__init__runs when you callPerson("Alice", 30)- The arguments
"Alice"and30are passed into__init__(self, name, age) - Inside
__init__, these values are stored on the object asself.nameandself.age - After initialization, you can access those attributes on each object
Why `self` Is the First Parameter
You’ll always see self as the first parameter of __init__ (and other methods).
Inside a class method:
selfrefers to the specific object being created or used- When you create an object with
p1 = Person("Alice", 30), Python secretly does something like: - Create an empty
Personobject - Call
Person.__init__(p1, "Alice", 30)
So inside __init__, self is p1 for the first object and p2 for the second, and so on.
You do not pass self yourself:
# Correct
p1 = Person("Alice", 30)
# Incorrect – do NOT pass self
# p1 = Person(self, "Alice", 30)Storing Values in Attributes
The main job of __init__ is to set up attributes on the new object, usually using self.attribute_name = value.
class Rectangle:
def __init__(self, width, height):
self.width = width # attribute
self.height = height # attribute
r = Rectangle(5, 10)
print(r.width) # 5
print(r.height) # 10Here:
widthandheightare parameters to__init__self.widthandself.heightare attributes on the object- You can give them the same names, but they are not the same thing:
- Left side:
self.width→ belongs to the object - Right side:
width→ parameter value passed in
Using Default Values in `__init__`
You can give parameters in __init__ default values, so they become optional when creating an object.
class Person:
def __init__(self, name, age=18, country="Unknown"):
self.name = name
self.age = age
self.country = country
p1 = Person("Alice") # uses default age and country
p2 = Person("Bob", 25) # sets age, default country
p3 = Person("Charlie", 40, "Australia") # sets all three
print(p1.age, p1.country) # 18 Unknown
print(p2.age, p2.country) # 25 Unknown
print(p3.age, p3.country) # 40 AustraliaThis lets you create flexible objects while still keeping sensible defaults.
Using Keyword Arguments with `__init__`
When you have multiple parameters, using keyword arguments when creating objects makes the code clearer:
class Car:
def __init__(self, brand, model, year, electric=False):
self.brand = brand
self.model = model
self.year = year
self.electric = electric
car1 = Car("Toyota", "Corolla", 2020)
car2 = Car(brand="Tesla", model="Model 3", year=2022, electric=True)
print(car1.electric) # False
print(car2.electric) # True
Here, Car(...) and Car(brand="Tesla", ...) both call __init__, but using keywords makes the meaning of each argument obvious.
Objects Without Custom `__init__`
You don’t have to define __init__ in every class. If you skip it, Python uses a default one that doesn’t take any extra arguments.
class Empty:
pass
e = Empty() # works
# e = Empty(10) # error: takes no arguments
If your class doesn’t need any initial data, you can leave out __init__, but in practice most useful classes will define one.
Adding Logic Inside `__init__`
__init__ can do more than just store values. It can:
- Calculate values
- Validate arguments
- Transform input before storing
class BankAccount:
def __init__(self, owner, balance):
if balance < 0:
raise ValueError("Balance cannot be negative")
self.owner = owner
self.balance = balance
account = BankAccount("Alice", 100)
print(account.balance) # 100
# account2 = BankAccount("Bob", -50) # would raise ValueErrorYou can also compute derived attributes:
class Circle:
def __init__(self, radius):
self.radius = radius
self.diameter = radius * 2 # computed attribute
c = Circle(5)
print(c.radius) # 5
print(c.diameter) # 10Common Mistakes with `__init__`
Here are typical beginner errors and how to fix them.
1. Forgetting `self` in the parameter list
class Person:
# Wrong: missing self
def __init__(name, age):
self.name = name # error: self is not defined
self.age = age
# Correct version:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age2. Forgetting to use `self.` when creating attributes
class Person:
def __init__(self, name, age):
name = name # Wrong: does not create an attribute
age = age # Just reassigns the local variables
# Correct:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
Without self., the values are lost after __init__ finishes.
3. Passing the wrong number of arguments
If __init__ expects some parameters, you must supply them (unless they have default values).
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# Wrong:
# p = Point(5) # missing y
# p = Point() # missing x and y
# Correct:
p = Point(5, 10)If you want flexibility, use defaults:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point() # (0, 0)
p2 = Point(5) # (5, 0)
p3 = Point(1, 2) # (1, 2)Example: A Simple Class Using `__init__`
Here’s a slightly more complete example that uses __init__ along with another method:
class Student:
def __init__(self, name, grades=None):
self.name = name
# Use an empty list if no grades are provided
if grades is None:
self.grades = []
else:
self.grades = grades
def average_grade(self):
if not self.grades:
return 0
return sum(self.grades) / len(self.grades)
s1 = Student("Alice", [90, 80, 85])
s2 = Student("Bob")
print(s1.name, s1.average_grade()) # Alice 85.0
print(s2.name, s2.average_grade()) # Bob 0
Here, __init__:
- Accepts optional
grades - Ensures every
Studentobject has agradesattribute - Provides a safe default (an empty list) when grades are not given
When to Use `__init__`
Use __init__ whenever you need to:
- Make sure every new object has certain attributes
- Require some information at creation time (like
name,age,width,height) - Set useful default values
- Validate incoming data or compute initial values
In most real-world classes, defining a clear and simple __init__ is one of the first steps in designing the class.