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:
- Factory Pattern: Suitable for scenarios requiring centralized object creation management
- Class Decorators: Supported in Python 2.6+, can elegantly modify class behavior
- 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.