Python is an object-oriented programming language, where everything is an object. Python provides several special methods, known as magic methods or dunder methods (dunder means double underscores), which enable powerful and flexible object-oriented capabilities. These methods start and end with double underscores (e.g., __init__, __add__, __len__, __repr__, etc.). One such powerful magic method is __new__, which plays an important role in instance creation.
What is __new__ in Python?
Whenever a class is instantiated, two methods are called:
- __new__ : Responsible for creating a new instance of the class.
- __init__ : Initializes the instance.
Unlike __init__, which is used for setting up an object after it has been created, __new__ is responsible for creating and returning the new instance itself.
Example:
Python
class A:
def __new__(cls):
print("Creating instance")
return super(A, cls).__new__(cls)
def __init__(self):
print("Initializing instance")
A()
OutputCreating instance
Initializing instance
Explanation: __new__ method is called first to create an instance of class A and then returns the newly created instance. After that, the __init__ method initializes the instance.
Syntax
class ClassName:
def __new__(cls, *args, **kwargs):
# Custom instance creation logic
instance = super(ClassName, cls).__new__(cls, *args, **kwargs)
return instance
Parameters:
- cls : The class itself.
- *args : Positional arguments passed to __new__ .
- **kwargs : Keyword arguments passed to __new__ .
Return Value:
- Must return an instance of the class (cls) or another class.
- If __new__ returns an object of another type, __init__ will not be called.
Alternative method
Instances can be created in __new__ using either:
instance = super(ClassName, cls).__new__(cls, *args, **kwargs)
or
instance = object.__new__(cls, *args, **kwargs)
If both __new__ and __init__ exist in a class, __new__ executes first. It determines whether __init__ should be executed by either returning an instance of the class or bypassing __init__ by returning an instance of another class.
Examples of __new__ method
Example 1: What happens if __new__ does not return an instance?
Python
class A:
def __new__(cls):
print("Creating instance")
def __init__(self):
print("Initializing instance")
print(A())
OutputCreating instance
None
Explanation: __new__ method does not return an instance of the class. Since __new__ must return an instance, but it lacks a return statement, it implicitly returns None.
Example 2: Returning a different type from __new__
Python
class A:
def __new__(cls):
print("Creating instance")
return "Hello, World!"
print(A())
OutputCreating instance
Hello, World!
Explanation : __new__ method should return a new object of class A, but here it returns the string "Hello, World!". Because of this, Python does not create an instance of A, so the __init__ method is never called. Instead, print(A()) simply prints "Hello, World!".
Example 3: Returning an instances of another class
Python
class GeeksforGeeks:
def __str__(self):
return "GeeksforGeeks Instance"
class Geek:
def __new__(cls):
return GeeksforGeeks()
def __init__(self):
print("Inside init")
print(Geek())
OutputGeeksforGeeks Instance
Explanation :__new__ method of Geek returns an instance of GeeksforGeeks instead of Geek, so the __init__ method is never called. When print(Geek()) is executed, it prints "GeeksforGeeks Instance" from the __str__ method of GeeksforGeeks.
Example 4: Returning values from __new__ and __init__
Python
class A(object):
def __new__(cls):
print("Creating instance")
return "GeeksforGeeks"
class B(object):
def __init__(self):
print("Initializing instance")
return "GeeksforGeeks"
print(A())
print(B())
Output
Hangup (SIGHUP)
Traceback (most recent call last):
File "/home/guest/sandbox/Solution.py", line 12, in <module>
print(B())
~^^
TypeError: __init__() should return None, not 'str'
Explanation: In class A, the __new__ method prints "Creating instance" and returns the string "GeeksforGeeks", so the __init__ method is never called. This results in "GeeksforGeeks" being printed. In class B, the __init__ method incorrectly tries to return a string, but since __init__ must return None, it raises a TypeError.
When to use __new__
__new__ is rarely overridden, but it is useful in specific scenarios, such as:
- Implementing Singleton Pattern: Ensures only one instance of a class exists.
- Returning Cached Objects: Helps in memory optimization by reusing existing objects instead of creating new ones.
- Immutable Object Creation: Used in classes like str and tuple since they are immutable.
- Subclassing Immutable Types: When extending built-in immutable types like int, float or str.