__init__ and __call__ In Python — What They Do And How To Use

Sachin Pal
5 min readFeb 28, 2023

--

__init__ and __call__ in Python
Source: Author(GeekPython)

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 create and initialize the objects of the class. When a class object is created, the task of the constructors is to assign values to the class's arguments.

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 two arguments called lang and year.

Then we created the instance of the class Language, passed it the necessary arguments, and called the function data.

Python was released in 1991

The arguments “Python” and “1991” passed inside the class are actually stored in the variables lang and year passed inside the __init__ that initializes the object.

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.

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

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

Syntax

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

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?

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.

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.

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 "1976" respectively. We accessed the lang and year by using the instance of the class object1.

C++
1985

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

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.

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.

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__ method.

__call__ inside Python classes

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

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.

Hello from constructor.
Hello from call.

Syntax

The syntax of the __call__ method is

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

Here,

self - reference of the object.

args and kwargs - arguments and keyword arguments.

__call__ with parameters

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.

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

__call__ with decorators

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.

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.

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.

That’s all for now

Keep Coding✌✌

--

--

Sachin Pal
Sachin Pal

Written by Sachin Pal

I am a self-taught Python developer who loves to write on Python Programming and quite obsessed with Machine Learning.

No responses yet