Python: __init__() is not the only constructor
Tags: python, beginners, tutorial
Published: 2019-12-15 15:31:24

Many beginners and even Pythonic Programmers think that `__init__()` is a constructor as it initializes values when it is called, but `__init__()` is a **special method** which initializes objects and it is immediately called when an object is created and initialized and does not allocate memory. So it is not the only method for the constructor.

```python
class Foo:
  def __init__(self, value_a, value_b):
    self.a = value_a
    self.b = value_b

foo = Foo(7, 9) # __init__ is called
print(foo.a , foo.b) # 7 , 9
```
`__new__()` method is called before `__init__()` acts. It is because`__new__()` allocates memory before `__init__()` is called.

```python
def __new__(cls, *args, **kwargs)
```

Things which are need to be kept in mind about `__new__()`:
- `__new__()` is always called before `__init__()`.
- First argument is the class itself which is passed implicitly.
- Always return a valid object from __new__(). Not mandatory, but that's the whole point.
- `__new__()` controls object creation. That means, in an extreme case, you can return a completely different type of object at the end. I will give an example soon.

```python
class Point():
    def __new__(cls,*args,**kwargs):
        print("From new")
        print(cls)
        print(args)
        print(kwargs)
        # create our object and return it
        obj = super().__new__(cls)
        return obj
    def __init__(self, x = 0, y = 0):
        print("From init")
        self.x = x
        self.y = y
```

Output:

```console
>>> p2 = Point(3,4)
From new
<class '__main__.Point'>
(3, 4)
{}
From init
```

We see that `__new__()` is called before `__init__()` when an object is initialized and can also see that the parameter `cls` in `__new__()` is the class itself (Point). Finally, the object is created by calling the `__new__()` method on object base class. In Python, object is the base class from which all other classes are derived. In the above example, we have done this using `super()`.

**When to use `__init__()` and `__new__()`?**

You would have seen `__init__()` being used  more frequently than `__new__()` as we mostly need to initialize objects not how the object is controlled though we can use  `__new__()` to initialize objects but it will make more sense to initialize them in `__init__()`. 

Some practical use of `__new__()` can be to restrict the number of objects created from a class, make a singleton and return a completely different object at the end.

## Restrict the number of objects created

Suppose we want to make a class `RectPoint` for creating instances to represent the four vertices of a square. We can inherit from our previous class Point (first example in this article) and use __new__() to implement this restriction. Here is an example to restrict a class to have only four instances.

```python
class RectPoint(Point):
    MAX_Inst = 4
    Inst_created = 0
    def __new__(cls,*args,**kwargs):
        if (cls.Inst_created >= cls.MAX_Inst):
            raise ValueError("Cannot create more objects")
        cls.Inst_created += 1
        return super().__new__(cls)
```
A sample run.

```console
>>> p1 = RectPoint(0,0)
>>> p2 = RectPoint(1,0)
>>> p3 = RectPoint(1,1)
>>> p4 = RectPoint(0,1)
>>> 
>>> p5 = RectPoint(2,2)
Traceback (most recent call last):
...
ValueError: Cannot create more objects
```

## A Simple Singleton

In this you have to be careful in `__init__()`, because it is called every time you “instantiate” the object (ie. you do `a = Example()`), even though they return the same object.


```python

_singleton = None

class Example:
    def __new__(cls):
        global _singleton

        if _singleton is None:
            _singleton = super(Example, cls).__new__(cls)

        return _singleton

a = Example()
b = Example()
a is b
# output: True
```

## Returning completely a new Object

In this example `int.__init__()` will be called, not `Example.__init__()`, as the returned object is not of type `Example` but `int`.

```python
class Example:
    def __new__(cls):
        return 3

type(Example())
# output: int
```

Thus, both `__new__()` and `__init__()` work and function as a constructor from other OOP language like C++ as it allocates and initializes objects. But as `__init__()` is mostly used a stated above. You can call `__init__() ` a "constructor" as we don't care about allocation. Python is a very abstracted language and we don't need to care about memory much. 

I hope my post clarifies this well and if there's any problem then please don't forget to comment.