Execution Order of __new__ and __init__ in Python with Design Pattern Applications

Nov 21, 2025 · Programming · 9 views · 7.8

Keywords: Python | Object-Oriented Programming | Design Patterns | Object Creation | Flyweight Pattern

Abstract: This article provides an in-depth exploration of the execution mechanism between __new__ and __init__ methods in Python, explaining why __init__ is always called after __new__. Through practical code examples demonstrating issues encountered when implementing the flyweight pattern, it offers alternative solutions using factory patterns and metaclasses. The paper details the distinct roles of these two methods in the object creation process, helping developers better understand Python's object-oriented programming mechanisms.

Core Principles of Python Object Creation Mechanism

In Python object-oriented programming, the object creation process follows a specific execution order. __new__ and __init__ are two key methods that play different roles in the object instantiation process. Understanding the execution order of these two methods is crucial for writing correct Python classes.

Fundamental Differences Between __new__ and __init__

The __new__ method is a static class method responsible for creating new instances of a class. It is the first step in the object creation process and must return an instance object. In contrast, __init__ is an instance method responsible for initializing the already created instance and does not return any value. This design ensures that an instance must be created first before it can be initialized.

From a technical implementation perspective, the __new__ method receives the class as its first parameter, while the __init__ method receives the instance itself as its first parameter. This means that when __new__ executes, the instance does not yet exist, whereas when __init__ executes, the instance has already been created through __new__.

Problem Analysis in Flyweight Pattern Implementation

Consider the following example code implementing the flyweight pattern:

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print("EXISTS")
            return A._dict['key']
        else:
            print("NEW")
            return super(A, cls).__new__(cls)

    def __init__(self):
        print("INIT")
        A._dict['key'] = self
        print("")

Executing a1 = A(), a2 = A(), a3 = A() produces the following output:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

This result reveals the core issue: even when __new__ returns an existing instance, Python still calls the __init__ method. This occurs because Python's object creation mechanism fixedly executes the sequence of __new__ followed by __init__, and cannot skip __init__ based on the return value of __new__.

Elegant Solutions Using Factory Pattern

To avoid implementing complex instance management logic in __new__, using the factory pattern is recommended. The factory pattern encapsulates object creation logic within independent factory classes or functions, maintaining class simplicity.

Here is an implementation example using a factory function:

class A(object):
    def __init__(self):
        print("INIT")
        # Normal initialization logic

class AFactory:
    _instances = {}
    
    @classmethod
    def get_instance(cls):
        if 'key' not in cls._instances:
            cls._instances['key'] = A()
        return cls._instances['key']

# Using factory to obtain instances
a1 = AFactory.get_instance()
a2 = AFactory.get_instance()
a3 = AFactory.get_instance()

This implementation avoids the complexity of overriding the __new__ method while providing a clear instance management mechanism. The factory pattern is particularly suitable for scenarios requiring control over object creation processes, such as singleton patterns, object pools, etc.

Advanced Applications with Metaclasses

For more complex scenarios, metaclasses can be used to completely control the class instantiation process. Metaclasses are classes of classes, and by overriding the metaclass's __call__ method, the entire instance creation flow can be customized.

Here is an example using metaclasses to implement the singleton pattern:

class Singleton(type):
    def __init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)
        self.__instance = None
    
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

class MyClass(metaclass=Singleton):
    def __init__(self):
        print("INIT")
        # Normal initialization logic

# Testing singleton behavior
obj1 = MyClass()
obj2 = MyClass()
print(obj1 is obj2)  # Output: True

The metaclass approach provides maximum flexibility but also increases code complexity. It is recommended only when complete control over class behavior is genuinely needed.

Best Practice Recommendations

In most cases, overriding the __new__ method should be avoided. Consider using __new__ only when subclassing immutable types (such as str, int, tuple) or when complete control over instance creation is required.

For instance management and sharing requirements, prioritize the following solutions:

  1. Factory Pattern: Suitable for scenarios requiring centralized object creation management
  2. Class Decorators: Supported in Python 2.6+, can elegantly modify class behavior
  3. Module-level Singletons: Manage singleton instances at the module level

Understanding the execution order of __new__ and __init__ helps in writing more robust Python code. Proper use of these features can improve code readability and maintainability while avoiding potential design pitfalls.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.