Table of Contents

What Will You Learn
In this tutorial, you'll learn what constructors are in Python and how they help you initialize new objects properly. You'll explore the __init__ method — Python's constructor — and see how to define it to set up instance variables during object creation. The guide walks you through examples of using parameters, setting default values, and avoiding common mistakes like shared mutable defaults. You'll also discover when to use __new__ and how constructors fit into the broader OOP workflow. By the end, you’ll have a clear understanding of how to create flexible, predictable Python objects from the start using constructors.


Understanding constructors is essential for anyone serious about writing clean, scalable code using object-oriented programming. Constructors define how your objects are created and initialized — it's the foundation of any class-based system. Without them, managing the internal state of an object would be manual, repetitive, and error-prone.

In the Python ecosystem, constructors are handled using the __init__() and __new__() methods.

These aren't just technical details — they're the gatekeepers to how objects come to life in your application. Mastering them means you can confidently control object creation, handle default values, or even implement object pooling. It's a key step in becoming a proficient developer.

What Are Constructors in Python?

A constructor in Python is a special method used to initialize a newly created object from a class. The most common constructor is __init__(), which is automatically called when a new object is instantiated. It sets up initial values for the object and prepares it for use. Python also supports a more low-level constructor called __new__(), which controls object creation itself. These methods are part of Python’s data model and are widely used in both simple scripts and complex systems.


    # Basic constructor with __init__
    class Person:
        def __init__(self, name):
            self.name = name

    p = Person("Alice")
    print(p.name)  # Output: Alice
  

In the example above, the __init__() method initializes the name attribute of the Person class. When you create a new instance of Person, Python automatically calls this method, allowing you to set up the object with the necessary data. This is the essence of what a constructor does: it prepares an object for use by initializing its attributes and ensuring it starts in a valid state.


    # Constructor with multiple arguments
    class Rectangle:
        def __init__(self, width, height):
            self.width = width
            self.height = height

    r = Rectangle(10, 5)
    print(r.width * r.height)  # Output: 50
  

In this example, the __init__() method takes two parameters, width and height, allowing you to create a rectangle with specific dimensions. This flexibility is one of the key benefits of using constructors in Python.


    # Using default values in constructor
    class Book:
        def __init__(self, title="Untitled"):
            self.title = title

    b = Book()
    print(b.title)  # Output: Untitled
  

Here, the __init__() method has a default value for the title parameter. If you create a Book object without providing a title, it will automatically be set to "Untitled". This feature allows for more flexible object creation, making constructors a powerful tool in Python programming.

What Makes a Constructor Useful?

Constructors simplify the creation of objects by automatically initializing them with the required data. Instead of manually assigning attributes after creating an object, you define all necessary logic in one place. This makes the code more maintainable and less error-prone. Constructors also support default values, which provides flexibility in how objects are instantiated.

More importantly, constructors help enforce data consistency. Every object created from a class will go through the same initialization logic, ensuring that no object is left in an incomplete or invalid state. This becomes critical in large applications where hundreds of objects are being created and manipulated.

Additionally, constructors support inheritance, allowing child classes to extend or modify initialization behavior while still relying on the parent structure. They can also be overloaded in Python using default values or alternative factory methods. In short, constructors serve as the backbone of any robust class design.

How to Make Constructors in Python?

To make a constructor in Python, you define a method called __init__() inside your class. This method is automatically executed when a new object of the class is created. It takes self as its first argument and can include any other parameters necessary for initializing the object’s attributes. You can also assign default values to parameters to make object creation more flexible. Python does not support multiple __init__ overloads like some other languages, but you can use conditional logic to achieve similar behavior. Below are three practical examples of how constructors work.


    # Basic constructor
    class Animal:
        def __init__(self, species):
            self.species = species

    dog = Animal("Canine")
    print(dog.species)  # Output: Canine
  

In this example, the __init__() method initializes the species attribute of the Animal class. When you create a new instance of Animal, Python automatically calls this method, allowing you to set up the object with the necessary data. This is the essence of what a constructor does: it prepares an object for use by initializing its attributes and ensuring it starts in a valid state.


    # Constructor with multiple parameters
    class Laptop:
        def __init__(self, brand, memory):
            self.brand = brand
            self.memory = memory

    macbook = Laptop("Apple", "16GB")
    print(macbook.brand)  # Output: Apple
  

Here, the __init__() method takes two parameters, brand and memory, allowing you to create a laptop object with specific attributes. This flexibility is one of the key benefits of using constructors in Python.


    # Constructor with default values
    class Course:
        def __init__(self, name="Python Basics", level="Beginner"):
            self.name = name
            self.level = level

    c = Course()
    print(c.name)   # Output: Python Basics
    print(c.level)  # Output: Beginner
  

In this example, the __init__() method has default values for the name and level parameters. If you create a Course object without providing these values, they will automatically be set to "Python Basics" and "Beginner". This feature allows for more flexible object creation, making constructors a powerful tool in Python programming.

Object Initialization With .__init__()

The __init__() method is Python’s primary constructor for initializing objects. When you call a class to create an instance, Python automatically runs __init__. Inside this method, you define how each instance should be set up by assigning values to attributes using the self keyword. This pattern ensures that every object starts its life in a well-defined state. You can also use logic within __init__ to validate or transform input. Here are three examples that show common use cases.


    # Example 1: Validating input
    class User:
        def __init__(self, age):
            if age < 0:
                raise ValueError("Age cannot be negative")
            self.age = age

    u = User(25)
    print(u.age)  # Output: 25
  

In this example, the __init__() method checks if the provided age is negative. If it is, a ValueError is raised, ensuring that the object cannot be created with invalid data. This kind of validation is crucial for maintaining data integrity in your application.


    # Example 2: Setting computed attributes
    class Circle:
        def __init__(self, radius):
            self.radius = radius
            self.area = 3.14 * radius * radius

    c = Circle(3)
    print(c.area)  # Output: 28.26
  

Here, the __init__() method calculates the area of a circle based on the provided radius. This allows you to have computed attributes that are automatically set when the object is created, making it easier to work with derived data.


    # Example 3: Using optional parameters
    class Profile:
        def __init__(self, username, verified=False):
            self.username = username
            self.verified = verified

    p = Profile("john_doe")
    print(p.verified)  # Output: False
  

In this example, the __init__() method has an optional parameter verified with a default value of False. This allows you to create a profile without explicitly setting the verification status, making the constructor more flexible.

Object Creation With .__new__()

The __new__() method in Python is a lower-level constructor that creates a new instance of a class. It is called automatically before __init__() and is responsible for allocating memory for the new object. This method is rarely used in everyday code, but becomes useful in advanced scenarios like subclassing immutable types (e.g., int, str) or implementing singletons. Unlike __init__, which modifies an existing instance, __new__ must return the new object. If you override __new__, you typically also define __init__ for initialization. Below are three examples that show how to use __new__ properly.


    # Example 1: Using __new__ in a basic class
    class Demo:
        def __new__(cls):
            print("Creating instance")
            return super().__new__(cls)
        
        def __init__(self):
            print("Initializing instance")

    d = Demo()
    # Output:
    # Creating instance
    # Initializing instance
  

In this example, the __new__() method is called first, printing "Creating instance". After that, __init__() is called, printing "Initializing instance". This shows the order of execution when creating a new object.


    # Example 2: Subclassing an immutable type
    class MyInt(int):
        def __new__(cls, value):
            return super().__new__(cls, value + 1)

    num = MyInt(4)
    print(num)  # Output: 5
  

Here, MyInt subclasses the built-in int type. The __new__() method modifies the value by adding 1 before returning the new instance. This is a common pattern when you need to customize the creation of immutable objects.


    # Example 3: Controlling object creation (e.g., singleton pattern)
    class Singleton:
        _instance = None

        def __new__(cls):
            if cls._instance is None:
                cls._instance = super().__new__(cls)
            return cls._instance

    a = Singleton()
    b = Singleton()
    print(a is b)  # Output: True
  

In this example, the __new__() method implements a singleton pattern, ensuring that only one instance of the Singleton class can exist. When you create multiple instances, they all refer to the same object, which is useful in scenarios where a single shared resource is needed.

How to Create Multiple Constructors in Python

Python does not support multiple __init__ methods directly like Java or C++. However, you can emulate multiple constructors using default arguments, class methods, or conditional logic inside __init__(). This approach lets you offer flexible initialization paths without breaking readability. Class methods are especially useful for creating alternative constructors with descriptive names. Below are three examples of different ways to achieve this pattern.


    # Example 1: Default arguments
    class User:
        def __init__(self, name="Guest"):
            self.name = name

    u = User()
    print(u.name)  # Output: Guest
  

In this example, the __init__() method has a default value for the name parameter. If you create a User object without providing a name, it will automatically be set to "Guest". This allows for flexible object creation while keeping the code clean and understandable.


    # Example 2: Conditional logic
    class Employee:
        def __init__(self, id, department=None):
            self.id = id
            self.department = department if department else "General"

    e = Employee(101)
    print(e.department)  # Output: General
  

Here, the __init__() method uses conditional logic to set a default department if none is provided. This allows you to create an Employee object with either a specific department or a general one, making the constructor versatile.


    # Example 3: Alternative constructor using classmethod
    class Book:
        def __init__(self, title, author):
            self.title = title
            self.author = author

        @classmethod
        def from_string(cls, info):
            title, author = info.split(";")
            return cls(title, author)

    b = Book.from_string("Python 101;John Doe")
    print(b.title)  # Output: Python 101
  

In this example, the from_string class method serves as an alternative constructor. It takes a single string argument, splits it into title and author, and then returns a new Book instance. This pattern is useful for creating objects from different types of input while keeping the main constructor focused on core attributes.

Differences Between __init__ and __new__

Both __init__ and __new__ are special methods in Python that deal with object creation, but they serve very different purposes. __new__ is responsible for creating and returning a new instance of the class, while __init__ initializes the already created instance. If you're subclassing immutable types like str or int, you’ll likely need to override __new__. In most day-to-day programming, however, beginners work only with __init__. Understanding the distinction is key to avoiding subtle bugs and mastering advanced object-oriented techniques.

Aspect __init__ __new__
Purpose Initializes an existing object Creates and returns a new object
Return type Returns None Must return an object
Call order Called after __new__ Called before __init__
Use case Used in nearly all class definitions Used when subclassing immutable types or customizing creation
Access to instance Receives the initialized instance via self Receives the class via cls
Frequency Very commonly used Rarely used

Common Beginner Mistakes with Constructors

Mistake 1: Forgetting to Define __init__ for Initialization

A common error is assuming that instance attributes appear automatically. If you don’t define an __init__ method and try to access attributes like self.name or self.age, you’ll hit an AttributeError. The constructor is essential for setting up the internal state of an object.


    # Mistake
    class User:
        pass

    u = User()
    print(u.name)  # AttributeError
  

Fix: Always use __init__ to initialize expected attributes.


    class User:
        def __init__(self, name):
            self.name = name

    u = User("Alice")
    print(u.name)  # Output: Alice
  

Mistake 2: Not Using self Properly

Beginners often forget that instance attributes must be assigned to self. Without self, the variable is treated as local to __init__ and not stored in the object. This leads to confusion when trying to access it later.


    # Mistake
    class Product:
        def __init__(self, price):
            price = price  # Does nothing useful

    p = Product(99)
    print(p.price)  # AttributeError
  

Fix: Use self.price = price to store data in the object.


    class Product:
        def __init__(self, price):
            self.price = price

    p = Product(99)
    print(p.price)  # Output: 99
  

Mistake 3: Misunderstanding Default Parameters

Some developers don't realize that default parameter values are only evaluated once. Using mutable defaults like lists or dicts can result in shared state between objects, which causes bugs that are hard to trace.


    # Mistake
    class Cart:
        def __init__(self, items=[]):
            self.items = items

    c1 = Cart()
    c2 = Cart()
    c1.items.append("apple")
    print(c2.items)  # Output: ['apple'] — unexpected!
  

Fix: Use None and create a new object inside __init__.


    class Cart:
        def __init__(self, items=None):
            self.items = items if items else []

    c1 = Cart()
    c2 = Cart()
    c1.items.append("apple")
    print(c2.items)  # Output: []
  

Mistake 4: Incorrect Argument Count

Beginners often call constructors with the wrong number of arguments, forgetting that self is implicit and not passed manually. This leads to TypeError at runtime.


    # Mistake
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y

    p = Point(10)  # TypeError
  

Fix: Ensure all required arguments are provided during object creation.


    p = Point(10, 20)
    print(p.x, p.y)  # Output: 10 20
  

Mistake 5: Overusing __new__ Unnecessarily

Beginners sometimes override __new__ without understanding its purpose, often copying examples without knowing when to use it. This leads to confusing, hard-to-maintain code. If you're not working with immutable types or singleton patterns, stick to __init__.


    # Mistake
    class Demo:
        def __new__(cls):
            print("Created")
            return super().__new__(cls)

        def __init__(self):
            print("Initialized")

    d = Demo()
  

Fix: Use __new__ only when you need custom object creation logic. For everything else, use __init__.

FAQ on Constructors

How many constructors can a class have in Python?

In Python, a class can have only one __init__ method, which acts as the constructor. Unlike Java or C++, Python doesn’t support true constructor overloading. However, you can simulate multiple constructors using default arguments or @classmethod.

This flexibility allows you to design readable and maintainable code without duplicating constructor logic. A common approach is creating alternative constructors with descriptive names, like from_string() or from_dict().


      class Book:
          def __init__(self, title):
              self.title = title

          @classmethod
          def from_string(cls, data):
              return cls(data.split(",")[0])
How can you identify special methods in Python such as constructors?

Special methods in Python are named with double underscores at the start and end, often called “dunder” methods. The most recognized one is __init__, used to initialize a new object.

Others include __str__ for string representation, __len__ for length, and __new__ for low-level object creation. You don’t call these methods directly — Python does it for you during specific operations.

Overriding them allows you to customize how your objects behave in different contexts, which is crucial for writing clean, powerful object-oriented code.

What’s the difference between default parameters and multiple constructors in Python?

Python doesn't support traditional constructor overloading. Instead, it uses default parameters or *args/**kwargs to mimic multiple constructors inside a single __init__ method.


    class User:
        def __init__(self, name="Guest"):
            self.name = name

For more complex logic, it's recommended to define named class methods like from_config() or from_json() using @classmethod. This approach is more readable and maintainable.

Is __init__ the only constructor in Python?

No. While __init__ is the primary initializer, the actual object creation is handled by __new__. It's called before __init__ and is responsible for returning a new instance of the class.


    class A:
        def __new__(cls):
            print("Creating instance")
            return super().__new__(cls)

        def __init__(self):
            print("Initializing instance")

You typically override __new__ only when working with immutable types or advanced design patterns like singletons.

Can I call __init__ manually in Python?

Technically, yes — but it's discouraged. The __init__ method is automatically called when you instantiate an object using ClassName(). Manually calling it can lead to unexpected results, especially if the object was already initialized.


      class Product:
          def __init__(self, name):
              self.name = name

      p = Product("Book")
      p.__init__("Pen")  # Not recommended

If you need to reassign values, define a dedicated method like reset(). Let Python manage object initialization to avoid logic bugs and confusion.