Unit 5 · Advanced Topics
Constructors (__init__)
Deep dive into Python constructors — the __init__ method, parameterized vs default constructors, the self parameter, destructor __del__, and constructor overloading with default arguments.
Table of Contents
What is a Constructor?
University Definition
A constructor is a special method in a class that is automatically called when an object of that class is created. Its primary purpose is to initialize the object's attributes with initial values. In Python, the constructor method is named __init__.
Constructor Lifecycle: ====================== 1. Object is created: obj = MyClass(args) 2. __new__ allocates memory 3. __init__ initializes attributes 4. Object is returned to variable When is constructor called? - Only ONCE per object creation - Automatically (you don't call it directly) - Before any other method on that object
__init__ Method
class Student:
def __init__(self, name, age):
print("__init__ called!") # Runs automatically
self.name = name
self.age = age
print("Creating object...")
s = Student("Rahul", 20) # __init__ is called here
print("Object created!")
print(f"Name: {s.name}, Age: {s.age}")
# Note: You do NOT call __init__ directly
# Student.__init__(s, "Priya", 21) # Works but not recommended
Output:
Creating object... __init__ called! Object created! Name: Rahul, Age: 20
Parameterized Constructor
University Definition
A parameterized constructor is a constructor that accepts arguments to initialize the object's attributes with specific values provided during object creation.
class Employee:
def __init__(self, name, emp_id, department, salary):
self.name = name
self.emp_id = emp_id
self.department = department
self.salary = salary
def display(self):
print(f"ID: {self.emp_id}")
print(f"Name: {self.name}")
print(f"Department: {self.department}")
print(f"Salary: {self.salary}")
# Creating objects with parameters
emp1 = Employee("Rahul", "E001", "IT", 50000)
emp2 = Employee("Priya", "E002", "HR", 45000)
print("Employee 1:")
emp1.display()
print("\nEmployee 2:")
emp2.display()
# Constructor with validation
class Circle:
def __init__(self, radius):
if radius < 0:
print("Warning: Negative radius! Setting to 0.")
self.radius = 0
else:
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
c1 = Circle(5)
c2 = Circle(-3)
print(f"\nCircle 1 area: {c1.area()}")
print(f"Circle 2 area: {c2.area()}")
Output:
Employee 1: ID: E001 Name: Rahul Department: IT Salary: 50000 Employee 2: ID: E002 Name: Priya Department: HR Salary: 45000 Warning: Negative radius! Setting to 0. Circle 1 area: 78.5 Circle 2 area: 0.0
Default Constructor
University Definition
A default constructor is a constructor that takes no parameters (except self). If you don't define any constructor in a class, Python provides a default constructor that does nothing.
# Class without __init__ - Python provides a default constructor
class Empty:
pass
obj = Empty()
print("Object created:", obj)
# Explicit default constructor
class Config:
def __init__(self):
self.host = "localhost"
self.port = 8080
self.debug = True
def display(self):
print(f"Host: {self.host}")
print(f"Port: {self.port}")
print(f"Debug: {self.debug}")
config = Config() # No arguments needed
config.display()
# Default constructor sets initial state
class Counter:
def __init__(self):
self.count = 0
print("Counter initialized to 0")
def increment(self):
self.count += 1
return self.count
c = Counter()
print(c.increment()) # 1
print(c.increment()) # 2
print(c.increment()) # 3
Output:
Object created: <__main__.Empty object at 0x...> Host: localhost Port: 8080 Debug: True Counter initialized to 0 1 2 3
self Parameter Explained
class Book:
def __init__(self, title, author):
self.title = title # self.title = attribute
self.author = author
def get_info(self):
return f"'{self.title}' by {self.author}"
# When you call:
book = Book("Python 101", "John")
# Python internally does:
# Book.__init__(book, "Python 101", "John")
# So 'self' = book, 'title' = "Python 101", 'author' = "John"
print(book.get_info())
# Verify self is the object
print(f"book is: {id(book)}")
class Demo:
def __init__(self, value):
self.value = value
print(f"self is object: {self is obj}")
obj = Demo(42) # self is object: True
# self is NOT a keyword - you can rename it (but don't!)
class Weird:
def __init__(anything, x):
anything.x = x # Works but bad practice!
w = Weird(10)
print(w.x) # 10
Output:
'Python 101' by John book is: 140234567890 self is object: True 10
Constructor with Default Values
class Player:
def __init__(self, name, level=1, health=100, score=0):
self.name = name
self.level = level
self.health = health
self.score = score
def __str__(self):
return (f"Player({self.name}, Lv:{self.level}, "
f"HP:{self.health}, Score:{self.score})")
# Using different combinations of defaults
p1 = Player("Rahul") # All defaults
p2 = Player("Priya", level=5) # Custom level only
p3 = Player("Amit", level=3, health=80) # Custom level & health
p4 = Player("Neha", 10, 200, 5000) # All custom
print(p1) # Player(Rahul, Lv:1, HP:100, Score:0)
print(p2) # Player(Priya, Lv:5, HP:100, Score:0)
print(p3) # Player(Amit, Lv:3, HP:80, Score:0)
print(p4) # Player(Neha, Lv:10, HP:200, Score:5000)
# Keyword arguments for flexibility
p5 = Player(name="Sneha", score=1000, level=7)
print(p5) # Player(Sneha, Lv:7, HP:100, Score:1000)
# Constructor with type hints
class Product:
def __init__(self, name: str, price: float, quantity: int = 1):
self.name = name
self.price = price
self.quantity = quantity
def total_cost(self) -> float:
return self.price * self.quantity
item = Product("Laptop", 50000, 2)
print(f"Total: {item.total_cost()}")
Output:
Player(Rahul, Lv:1, HP:100, Score:0) Player(Priya, Lv:5, HP:100, Score:0) Player(Amit, Lv:3, HP:80, Score:0) Player(Neha, Lv:10, HP:200, Score:5000) Player(Sneha, Lv:7, HP:100, Score:1000) Total: 100000
Destructor __del__
University Definition
A destructor is a special method (__del__) that is called automatically when an object is about to be destroyed (garbage collected). It is used for cleanup activities like closing files or releasing resources.
class Resource:
def __init__(self, name):
self.name = name
print(f"Resource '{self.name}' created (constructor)")
def __del__(self):
print(f"Resource '{self.name}' destroyed (destructor)")
# Creating and destroying objects
print("Creating resources...")
r1 = Resource("Database")
r2 = Resource("File")
print("Deleting r1...")
del r1 # __del__ called for r1
print("Program ending...")
# r2 will be destroyed when program ends
# __del__ will be called automatically
Output:
Creating resources... Resource 'Database' created (constructor) Resource 'File' created (constructor) Deleting r1... Resource 'Database' destroyed (destructor) Program ending... Resource 'File' destroyed (destructor)
University Exam Tip
Constructor vs Destructor: The constructor (__init__) runs when an object is created; the destructor (__del__) runs when an object is destroyed. Constructor initializes resources; destructor cleans them up. In Python, the destructor is called by the garbage collector, not explicitly.
Constructor Overloading
University Definition
Python does not support traditional constructor overloading (multiple __init__ with different signatures). Instead, it achieves similar functionality using default arguments, variable arguments (*args), and keyword arguments (**kwargs).
# Python does NOT support multiple __init__ methods
# class Demo:
# def __init__(self):
# self.x = 0
# def __init__(self, x): # This OVERWRITES the first one!
# self.x = x
# SOLUTION 1: Default arguments
class Flexible:
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
f1 = Flexible() # (0, 0, 0)
f2 = Flexible(5) # (5, 0, 0)
f3 = Flexible(5, 10) # (5, 10, 0)
f4 = Flexible(5, 10, 15) # (5, 10, 15)
print(f1.x, f1.y, f1.z) # 0 0 0
print(f4.x, f4.y, f4.z) # 5 10 15
# SOLUTION 2: *args (variable positional arguments)
class MultiInit:
def __init__(self, *args):
if len(args) == 0:
self.data = []
elif len(args) == 1:
self.data = list(args[0]) if isinstance(args[0], (list, tuple)) else [args[0]]
else:
self.data = list(args)
m1 = MultiInit()
m2 = MultiInit([1, 2, 3])
m3 = MultiInit(1, 2, 3)
print(m1.data) # []
print(m2.data) # [1, 2, 3]
print(m3.data) # [1, 2, 3]
# SOLUTION 3: **kwargs (variable keyword arguments)
class Config:
def __init__(self, **kwargs):
self.settings = kwargs
def display(self):
for key, value in self.settings.items():
print(f" {key}: {value}")
c1 = Config()
c2 = Config(host="localhost", port=8080)
c3 = Config(host="0.0.0.0", port=3000, debug=True)
c3.display()
Output:
0 0 0 5 10 15 [] [1, 2, 3] [1, 2, 3] host: 0.0.0.0 port: 3000 debug: True
Common Mistakes to Avoid
- Defining multiple
__init__methods — the last one overwrites all previous ones. - Forgetting to include
selfas the first parameter of__init__. - Calling
__init__manually — it is called automatically during object creation. - Forgetting that
__del__timing is uncertain — relies on garbage collection. - Using mutable default arguments (lists, dicts) — they are shared between calls.
Practice Questions
- Create a
Rectangleclass with a constructor that accepts length and width with default values of 1. Add a destructor that prints a farewell message. - Implement a
Calculatorclass that can be initialized with 0, 1, or 2 numbers using default arguments. - Write a
FileHandlerclass with __init__ (open file) and __del__ (close file) methods demonstrating resource management. - Demonstrate constructor overloading using *args to create a
Pointclass that accepts 1D, 2D, or 3D coordinates.
Key Points
__init__ is the constructor — called automatically when an object is created.
Default constructor takes no arguments (except self) and sets initial state.
Parameterized constructor accepts arguments to customize object initialization.
Python does not support multiple constructors — use default arguments instead.
__del__ is the destructor — called when the object is garbage collected.
self must always be the first parameter but is never passed explicitly.