You must have seen the implementation of the __init__ method in any Python class, and if you have worked with Python classes, you must have implemented the __init__ method many times. However, you are unlikely to have implemented or seen a __new__ method within any class.
In this article, we’ll see:
- Definition of the
__init__and__new__methods __init__method and__new__method implementation- When they should be used
- The distinction between the two methods
__init__ Vs __new__ Method
The __init__ method is an initializer method that is used to initialize the attributes of an object after it is created, whereas the __new__ method is used to create the object.
When we define both the __new__ and the __init__ methods inside a class, Python first calls the __new__ method to create the object and then calls the __init__ method to initialize the object’s attributes.
Most programming languages require only a constructor, a special method to create and initialize objects, but Python has both a constructor and an initializer.
Let’s talk about both these methods one by one and implement these methods inside a Python class.
__new__ Method
As stated already, the __new__ method is a constructor method used to create and return an object(instance of the class).
Syntax
object.__new__(cls, *args, **kwargs)
The __new__ method’s first parameter is cls, which is a class of the object we want to create.
The *args and **kwargs parameters are not used by the __new__ method, but they must match the parameters of the class’s __init__ method.
Example
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Defined a base class class Name: # Created a __new__ method def __new__(cls): print(f'Called the __new__ method.') return super(Name, cls).__new__(cls) # Created an __init__ method def __init__(self): print(f"Called the __init__ method.") # Created an object Name() |
In the above code, we defined the __new__ and __init__ methods within the class Name. The __new__ method accepts the cls parameter, which is used to refer to the class Name, and when called, it prints the message and returns the class instance using the super(Name, cls).__new__(cls).
One thing to note is that the Name class is a base class, so we could have directly called the __new__ method on the object like this expression object.__new__(cls). However, the standard method is to use the super() function.
The __init__ method is then called with the instance passed to the self parameter.
Then we called the Name class (Name()), and when we run the code, we get the output shown below.
|
1 2 |
Called the __new__ method. Called the __init__ method. |
The output shows that the __new__ method is called first and then the __init__ method.
__init__ Method
As we saw in the above example, the __init__ method is called to initialize the attributes of the object as soon as the object is created.
Syntax
__init__(self, *args, **kwargs)
As a first parameter, the __init__ method accepts self, which is used to refer to the class instance.
The parameters *args and **kwargs are used to initialize the instance variable with the values stored within them.
Example
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Defined a base class class Name: # Created a __new__ method def __new__(cls, name): print(f'Called the __new__ method.') return super(Name, cls).__new__(cls) # Created an __init__ method def __init__(self, name): print(f"Called the __init__ method.") self.name = name # Created an object name_obj = Name('Sachin') print(name_obj.name) |
In the __init__ method, we passed the name parameter and did the same in the __new__ method to make the __new__ and __init__ method signature compatible with each other.
We called the class with the 'Sachin' argument, which will automatically invoke the __init__ method and will initialize the instance variable self.name with this value.
When we call the name attribute(instance variable) on the object name_obj, we’ll get the following output.
|
1 2 3 |
Called the __new__ method. Called the __init__ method. Sachin |
The name attribute(instance variable) of the name_obj is initialized to the value 'Sachin'.
Implementation
Let’s define both __new__ and __init__ methods inside the class Language.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Language: def __new__(cls, *args): return super().__new__(cls) def __init__(self, lang, year): self.lang = lang self.year = year language = Language('Python', 1991) print(language.lang) print(language.year) ---------- Python 1991 |
We defined both __new__ and __init__ methods inside the class Language and created the class object, when we run the code, Python will call the __new__ method which is responsible for creating and returning the object of the class, and then calls the __init__ method which is responsible for the initialization of the object’s attributes(instance variables).
Now we can access the attributes of the object lang and year using dot notation on the object language as we did in the above code.
Every time we create a new object, the __init__ method is invoked, which means that if we don’t return super().__new__(cls), then the __init__ method will not execute and return None.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Language: def __new__(cls, *args): print("Creating") # Method not called def __init__(self, lang, year): print("Initializing") self.lang = lang self.year = year language = Language('Python', 1991) print(language) ---------- Creating None |
Let’s see what happens when we implement only the __init__ method inside a class.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Language: def __init__(self, lang, year): self.lang = lang self.year = year language = Language('Python', 1991) print(language.lang) print(language.year) ---------- Python 1991 |
The code works the same as the previous code which we saw at the beginning of this section.
When we instantiated the class using language = Language('Python', 1991), the expression is equivalent to the following:
|
1 2 |
language = object.__new__(Language) language.__init__('Python', 1991) |
If we try to print the language object after calling the __new__ and the __init__ methods using the __dict__, then we’ll get the following output:
|
1 2 3 4 5 6 7 8 |
language = object.__new__(Language) print(language.__dict__) language.__init__('Python', 1991) print(language.__dict__) ---------- {} {'lang': 'Python', 'year': 1991} |
We got an empty dictionary after calling the __new__ method because the object was created but not yet initialized, to initialize, we called the __init__ method explicitly and got the values.
When To Use
Use case of __new__
Consider the following example in which we are using the __new__ method to customize the object at the instantiation.
|
1 2 3 4 5 6 |
class Reverse(str): def __new__(cls, sequence): return super().__new__(cls, sequence[::-1]) seq = Reverse("GeekPython") print(seq) |
The above code defines the class Reverse, which inherits from the str built-in type, as well as the __new__ method that accepts a sequence. We override the __new__ method to reverse the sequence before creating the object.
|
1 |
nohtyPkeeG |
The argument "GeekPython" passed to the Reverse class got reversed due to sequence[::-1] before the object is created.
This can’t be done using the __init__ method, if we try to do so, the result will be an error.
|
1 2 3 4 5 6 7 8 9 |
class Reverse(str): def __init__(self, sequence): super().__init__(sequence[::-1]) seq = Reverse("GeekPython") print(seq) ---------- TypeError: object.__init__() takes exactly one argument (the instance to initialize) |
Another use case of the __new__ method is creating a Singleton (design pattern that restricts the instantiation of a class to a single instance).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Singleton: # Created a private variable __ins = None # Defined the __new__ method def __new__(cls): if cls.__ins is None: print("Instance creating...") cls.__ins = super().__new__(cls) return cls.__ins # Creating object obj1 = Singleton() obj2 = Singleton() obj3 = Singleton() print(obj1) print(obj2) print(obj3) # Checking if they are all same print(obj1 is obj2 is obj3) |
In the above code, we defined the class Singleton and created a private variable __obj to store the class’s single instance, as well as a __new__ method that checks if the __ins is None, then creates a new instance and assigns it to the __ins, and returns the existing instance if the __ins is not None.
Then we printed three instances of the Singleton class named obj1, obj2, and obj3 and checked to see if they were all the same.
|
1 2 3 4 5 |
Instance creating... <__main__.Singleton object at 0x000001B3DFD5C130> <__main__.Singleton object at 0x000001B3DFD5C130> <__main__.Singleton object at 0x000001B3DFD5C130> True |
All three instances point to the same memory address, and we can see that we got True, indicating that they are all the same.
Use case of __init__
The __init__ method is commonly used to initialize the object’s attributes with or without the default values.
|
1 2 3 4 5 6 7 8 9 10 |
class Language: def __init__(self, lang="Python", year=1991): self.lang = lang self.year = year def show(self): print(f'Language: {self.lang} | Founded: {self.year}.') language = Language() language.show() |
The above code defines a Language class and the __init__ method, which accepts lang and year parameters with default values of "Python" and 1991, respectively.
When we call the Language class without argument, the __init__ method will set the lang and year attributes to their default values.
|
1 |
Language: Python | Founded: 1991. |
Difference
Now that we’ve seen the definition, syntax, and implementation of both methods, we are now able to differentiate between them.
| __new__ method | __init__ method |
The __new__ method is called first | The __init__ method is called after the __new__ method |
| Used to create and return the object | Used to initialize the attributes of the object |
| It is a constructor method | It is an initializer method |
| Takes class as the first parameter | Takes the instance of the class as the first parameter |
| Can be overridden to customize the object at the instantiation | Probably only be used to initialize the attributes of the object |
Conclusion
Python has a concept of a constructor and an initializer method. The __new__ method is a constructor method whereas the __init__ method is an initializer method. Python first calls the __new__ method which is responsible for the object creation and then calls the __init__ method which is responsible for the initialization of the object’s attributes.
🏆Other articles you might be interested in if you liked this one
✅Context managers and the with statement in Python.
✅What is abstract base class(ABC) in Python?
✅Public, Protected, and Private access modifiers in Python.
✅What are inheritance and different types of inheritance in Python?
✅What is enumerate() function in Python?
✅Execute dynamically generated code using the exec() in Python.
✅Async/Await – Asynchronous programming using asyncio in Python.
That’s all for now
Keep Coding✌✌
