__init__ Vs __new__ - When to Use and How
Tags: python, oop, programming, tutorial
Published: 2023-08-13 08:43:12

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.

{% embed https://geekpython.in/understanding-args-and-kwargs-in-python-best-practices-and-guide %}

**Example**

```python
# 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.

{% embed https://geekpython.in/super-in-python %}

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.

```bash
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.

{% embed https://geekpython.in/init-and-call-method %} 

**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**

```python
# 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](https://python-forge.readthedocs.io/en/latest/glossary.html#term-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.

```bash
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`.

```python
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`.

```python
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.**

```python
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:

```python
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:

```python
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.

```python
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.

```bash
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.

```python
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](https://en.wikipedia.org/wiki/Singleton_pattern) ***(design pattern that restricts the instantiation of a class to a single instance).***

```python
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.

```bash
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.

```python
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.

```bash
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](https://geekpython.in/context-managers-and-python-with-statement).

✅[What is abstract base class(ABC) in Python](https://geekpython.in/abc-in-python)?

✅[Public, Protected, and Private access modifiers in Python](https://geekpython.in/access-modifiers-in-python).

✅[What are inheritance and different types of inheritance in Python](https://geekpython.in/class-inheritance-in-python)?

✅[What is enumerate() function in Python](https://geekpython.in/python-enumerate-function-with-example-beginners-guide)?

✅[Execute dynamically generated code using the exec() in Python](https://geekpython.in/exec-function-in-python).

✅[Async/Await - Asynchronous programming using asyncio in Python](https://geekpython.in/asyncio-how-to-use-asyncawait-in-python).

---

**That's all for now**

**Keep Coding✌✌**