# __init__ and __call__ In Python - What Do They Do?

You may have encountered the methods in Python that are prefixed and suffixed with **double underscores**, those methods are called **"Dunder Methods"**. These methods are also called **"magic methods"**.

**Dunder methods** are used to overload specific methods in order to make their behaviour unique to that class.

Python has a rich collection of built-in dunder methods and a few are listed below:

* **\_\_init\_\_**
    
* **\_\_call\_\_**
    
* **\_\_repr\_\_**
    
* **\_\_str\_\_**
    
* **\_\_len\_\_**
    
* **and more...**
    

In this article, we will look at two dunder methods(`__init__` and `__call__`) that are commonly used in Python classes. How do they differ, and when do we need to use them within the Python program?

# `__init__` Method

The `__init__` method is used to initialize the object's attributes after the object of the class is created.

```python
class Language:
    def __init__(self, lang, year):
        self.lang = lang
        self.year = year

    def data(self):
        print(f"{self.lang} was released in {self.year}")

object = Language("Python", "1991")
object.data()
```

The `__init__` for the class `Language` is created in the preceding code, and it takes the parameters `lang` and `year`.

Then we created the instance of the class `Language`, passed it the necessary arguments, and called the method `data` on the instance `object`.

```bash
Python was released in 1991
```

The arguments `"Python"` and `"1991"` passed to the class are actually stored in the parameters `lang` and `year` passed to the `__init__` method, which initializes the instance variables (`self.lang` and `self.year`) with these values.

Every time we create an object, the `__init__` method is automatically invoked. We'll get different results if we create another object and pass different values.

```python
object1 = Language("JavaScript", "1995")
object1.data()

----------
JavaScript was released in 1995
```

![init visualization](https://cdn.hashnode.com/res/hashnode/image/upload/v1683895844022/149fb978-9949-4536-9e8d-8b1f902110fb.png align="center")

## Syntax

Thus, we can conclude that the syntax of the `__init__` method can be written as the following.

```python
class SomeClass:
    def __init__(self, arg1, arg2, ...)
        # constructor body
```

Here,

`self` - is an instance of the class. Mandatory.

`arg1` and `arg2` - are the parameters. We can pass as many parameters as we want or the field can also be left empty.

**What if we pass more than the number of parameters that a class takes?**

```python
class Language:
    def __init__(self, lang):
        self.lang = lang

object1 = Language("JavaScript", "1995")
```

The above code will throw an error and prompts the following message.

```python
Traceback (most recent call last):
  ...
    object1 = Language("JavaScript", "1995")
TypeError: __init__() takes 2 positional arguments but 3 were given
```

The message states that two arguments were allowed, but three were passed. But we passed two arguments, not three then why did this happen?

This occurred because `__init__` only accepts `self` and `lang`. When we instantiated the class with arguments, the keyword `self`, which represents the object's instance, was passed along with the arguments automatically.

So, when we passed the arguments `"JavaScript"` and `"1995"`, the `self` was automatically passed, making three arguments passed to the class `Language`.

## `__init__` with and without parameters

Python constructor(`__init__`) can be created with or without passing any parameters.

### Default `__init__` constructor

**A constructor created without parameters other than** `self`**(a reference to the instance being constructed) is called the default constructor.**

```python
class Language:
    def __init__(self):
        self.lang = "C++"
        self.year = 1985

object1 = Language()
print(object1.lang)
print(object1.year)
```

We created `self.lang` and `self.year` and assigned the default values `"C++"` and `"1985"` respectively. We accessed the `lang` and `year` by using the instance of the class `object1`.

```python
C++
1985
```

We can also override the attribute's default value by assigning a new value before accessing the attribute from the class.

```python
class Language:
    def __init__(self):
        self.lang = "C++"
        self.year = 1985

object1 = Language()
# Assigned new value to the lang
object1.lang = "Python"
print(object1.lang)
print(object1.year)

----------
Python
1985
```

### `__init__` with parameters

We've already seen some examples where we used parameters to create the constructor. Pass the parameters to the constructor, as shown in the following example. Then we created an object or instance of the `Vehicle` class and passed the arguments to it. The output was then obtained by calling the `vehicle` function.

```python
class Vehicle:
    def __init__(self, name, model):
        self.name = name
        self.model = model

    def vehicle(self):
        print(f"Brand: {self.name} and Model: {self.model}")

bmw_car = Vehicle("BMW", "X5")
audi_car = Vehicle("Audi", "A4")

bmw_car.vehicle()
audi_car.vehicle()

----------
Brand: BMW and Model: X5
Brand: Audi and Model: A4
```

# `__call__` method

When we invoke a function, we simply use the function name with parenthesis, such as `hello()`, to notify the interpreter that a function is being called. Well, we can say that it is a shorthand for `hello.__call__()`.

When we invoke a function in Python, the interpreter executes the `__call__` method in the background.

```python
def func(a, b):
    print(a + b)

func(32, 8)
func.__call__(8, 9)

----------
40
17
```

In the above code, first, we called the function `func` simply as we usually do and then by using the `__call__`.

## `__call__` inside Python classes

The concept behind using `__call__` is to call the instances of the classes as if they were a function. **Instances of classes can be made callable by defining a** `__call__` **method in their class.**

```python
class Demo:
    def __init__(self):
        print("Hello from constructor.")

    def __call__(self):
        print("Hello from call.")

example = Demo()
example()
```

In this case, we called the class object `example` as if it were a function.

```python
Hello from constructor.
Hello from call.
```

## Syntax

The syntax of the `__call__` method is

```python
object.__call__(self, *args, **kwargs)
```

Here,

`self` - reference of the object.

`args` and `kwargs` - arguments and keyword arguments.

## `__call__` with parameters

```python
class Student:
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def __call__(self, school):
        print(f"The id of {self.name} is {self.id}.")
        print(f"The school name is {school}.")

detail = Student(45, "Sachin")
detail("GSS")
```

We passed the parameter `school` to the `__call__` method just like we do when we create the constructor. This will allow us to pass the argument within the object of the class as we did in the above code.

```python
The id of Sachin is 45.
The school name is GSS.
```

## `__call__` with decorators

```python
class Demo:
    def __init__(self, action):
        self.action = action
        print("Hello from constructor.")

    def __call__(self):
        self.action()
        print("Hello from call.")

@Demo
def decor():
    print("Called using decorator.")

decor()
```

We first generated the decorator(`@Demo`) for the class `Demo`, followed by the function `decor`. Then we invoked the `decor` function and got the result shown below.

```python
Hello from constructor.
Called using decorator.
Hello from call.
```

The decorator altered the behaviour of our class `Demo`, and we accessed the class's attributes simply by invoking the `decor` function.

If we examine more closely, the function `decor` was supplied as an argument to the class `Demo`, the **decor's** return value was saved within the `action` variable, and the class was called and produced the output.

**The decorator part in the above code is equivalent to the following.**

```python
def decor():
    print("Called using decorator.")

decor = Demo(decor)
```

# Conclusion

The `__init__` method is also called the constructor method which is used to initialize the objects of the specific class whereas the `__call__` method allows us to call the object of the class like a function.

The `__init__` method created without passing parameters is called the default `__init__` constructor.

When we call a function using `()`, in actuality, the `__call__` method is implemented in the function.

We've coded many examples to get a better understanding of both methods.

---

🏆**Other articles you might be interested in if you liked this one**

✅[Introduction to the different types of class inheritance in Python](https://geekpython.in/class-inheritance-in-python).

✅[Build a deep learning model using the transfer learning technique](https://geekpython.in/using-transfer-learning-for-deep-learning-model).

✅[Tackle the shortage of data by using the data augmentation technique](https://geekpython.in/data-augmentation-in-deep-learning).

✅[Upload and display images on the frontend using Flask in Python](https://geekpython.in/render-images-from-flask).

✅[Integrate TailwindCSS into the Flask app](https://geekpython.in/merging-tailwindcss-into-flask-apps).

✅[Avoid conflicts between dependencies by using virtual environments](https://geekpython.in/python-virtual-environments-venv).

---

**That's all for now**

**Keep Coding✌✌**
