CS Pathfinder Logo CS Pathfinder

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 self as 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

  1. Create a Rectangle class with a constructor that accepts length and width with default values of 1. Add a destructor that prints a farewell message.
  2. Implement a Calculator class that can be initialized with 0, 1, or 2 numbers using default arguments.
  3. Write a FileHandler class with __init__ (open file) and __del__ (close file) methods demonstrating resource management.
  4. Demonstrate constructor overloading using *args to create a Point class 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.

Python Programming Handwritten Notes

Master Python Programming with Easy Handwritten Notes – Perfect for Interviews, Placements, GATE & Exams.