Python __init__ Method
Last modified April 8, 2025
This comprehensive guide explores Python's __init__ method, the
special method responsible for object initialization. We'll cover basic usage,
inheritance, default values, multiple constructors, and practical examples.
Basic Definitions
The __init__ method is a special method in Python classes that
initializes newly created objects. It's called automatically after the object
is created by __new__.
Key characteristics: it must accept self as first parameter, doesn't
return anything, and is used to set initial values for object attributes. Unlike
constructors in other languages, it doesn't create the object.
Basic __init__ Implementation
Here's the simplest implementation showing how __init__ initializes
object attributes. This demonstrates the fundamental usage pattern.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Alice", 30)
print(f"{person.name} is {person.age} years old")
This example creates a Person class with name and age attributes.
The __init__ method sets these values when a new instance is created.
The self parameter refers to the instance being initialized.
Attributes are assigned to self to make them instance variables.
Default Parameter Values
__init__ can use default parameter values to make some arguments
optional while still allowing customization during initialization.
class Car:
def __init__(self, make, model, year=2023, color="black"):
self.make = make
self.model = model
self.year = year
self.color = color
def __str__(self):
return f"{self.year} {self.make} {self.model} ({self.color})"
car1 = Car("Toyota", "Camry")
car2 = Car("Ford", "Mustang", 2022, "red")
print(car1)
print(car2)
This Car class has required make and model parameters, with optional
year and color parameters that default to 2023 and "black" if not provided.
Default parameters make classes more flexible while reducing boilerplate code. They're especially useful when most instances share common default values.
Inheritance and __init__
When using inheritance, __init__ methods can be chained to properly
initialize parent class attributes along with child class attributes.
class Animal:
def __init__(self, species):
self.species = species
def __str__(self):
return f"I am a {self.species}"
class Dog(Animal):
def __init__(self, name, breed):
super().__init__("dog")
self.name = name
self.breed = breed
def __str__(self):
return f"{super().__str__()}, {self.name} the {self.breed}"
dog = Dog("Rex", "Labrador")
print(dog)
This example shows how to properly initialize parent class attributes using
super().__init__(). The Dog class extends Animal
while adding its own attributes.
The super() function returns a proxy object that delegates method
calls to the parent class. This ensures proper method resolution order.
Multiple Constructors with @classmethod
While Python doesn't support multiple constructors directly, you can simulate
them using @classmethod to create alternative initialization methods.
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@classmethod
def from_square(cls, side_length):
return cls(side_length, side_length)
@classmethod
def from_dict(cls, dimensions):
return cls(dimensions['width'], dimensions['height'])
def area(self):
return self.width * self.height
rect1 = Rectangle(4, 5)
rect2 = Rectangle.from_square(3)
rect3 = Rectangle.from_dict({'width': 2, 'height': 7})
print(rect1.area(), rect2.area(), rect3.area())
This Rectangle class shows three ways to create instances: through
the standard __init__, via a square factory method, and from a
dictionary of dimensions.
Class methods provide flexible initialization options while maintaining a single
__init__ method. Each factory method returns a new instance by
calling the class constructor.
Initializing Collections in __init__
When initializing mutable collections as instance attributes, it's important to create new collections for each instance to avoid shared state between instances.
class ShoppingCart:
def __init__(self, customer_name):
self.customer_name = customer_name
self.items = [] # New list for each instance
def add_item(self, item):
self.items.append(item)
def __str__(self):
return f"{self.customer_name}'s cart: {', '.join(self.items)}"
cart1 = ShoppingCart("Alice")
cart2 = ShoppingCart("Bob")
cart1.add_item("Book")
cart2.add_item("Shirt")
print(cart1)
print(cart2)
This example demonstrates proper initialization of instance-specific collections.
Each ShoppingCart gets its own empty list for items.
If the list was defined as a class variable instead, all instances would share the same list, leading to unexpected behavior when items are added.
Best Practices
- Keep __init__ simple: Focus on attribute initialization
- Avoid complex logic: Move business logic to other methods
- Use type hints: Document expected parameter types
- Initialize all attributes: Set defaults for optional ones
- Call super().__init__: In inheritance hierarchies
Source References
Author
List all Python tutorials.