The Dunders(Magic Methods) in Python

Ridwan Yusuf
3 min readJan 20, 2023

--

In the Python community, Dunder is a shortened word for Double Underscores(__), which are often used to define special methods.
Names or methods having both leading and trailing underscores(e.g. __samplename__) refers to special methods defined in the language. These methods are implicitly called under the hood.

This guide introduces some of them

1. __init__ : the __init__ method is mostly used in class declaration in python. It can be used as a constructor to initialize attributes specific
to each instance of a class. This method is automatically called when we attempt to create a real object(instance) from a Class(blueprint/template)

Aside its wide usage as a constructor, it can be utilized in turning a dictionary into an object where we can reference its key as an attribute of the object.
Let’s take a look at this simple scenario.

Say we have a python dictionary:

my_dict = {"name":"Ray", "email":"a@gmail.com", "user_id":1}

To access the value of a field in the dictionary, we would need to write my_dict[‘name’] or my_dict.get(‘name’)

Turning the dictionary into an object(instance)
[*]Snippet

class UserData:
def __init__(self, my_dict):
for key in my_dict:
setattr(self, key, my_dict[key])

#setattr(x, ‘y’, v) is equivalent to x.y = v
[*]Usage

my_dict = {"name":"Ray", "email":"a@gmail.com", "user_id":1}
user = UserData(my_dict)
print(user.name)
print(user.email)
print(user.user_id)

[*]Output
Ray
a@gmail.com
1

2. __call__: Recall that whenever we need to call a function, we would write as ‘name_of_function()’, what happens under the hood whenever you call a function is that the __call__ method provided by python get executed. What this means is that if we need to make an object callable(i.e. being able to be invoked or called), we need to implement the __call__ method.

In the previous code snippet, we created an object user:
[*]
user = UserData(my_dict)

Any attempt to call user() would lead to an error saying is not callable. To fix this, we can define the __call__ method
[*]Snippet

class UserData:
def __init__(self, my_dict):
for key in my_dict:
setattr(self, key, my_dict[key])

def __call__(self):
return 5

[*]Usage

my_dict = {"name":"Ray", "email":"a@gmail.com", "user_id":1}
user = UserData(my_dict)
print("Calling user::", user())

[*]Output
Calling user:: 5

3. __add__: This method is called when using + operator. When you say 1+2, it executes __add__ method provided by python. This translates to sum for numbers and concatenation for string. This leads us to another concept called ‘operator overloading’.
What this means is that whenever we create our own custom object, we can overload any operator(+,-,*,/, etc.). such that when the operator in used with our objects, __add__(+) or __sub__(-) which we have defined in our class would be executed.
[*] +

num_1 = 2
num_1 + 1

[*]Output
3
[*] __add__

num_1 = 2
num_1.__add__(1)

[*]Output
3

4. __str__: This is used to define the nicely printable string representation of an object of a class. It’s default behavior is to return the memory address of an object but can be overridden to meet our requirement.
[*]Snippet

class User:
pass

user1 = User()
print(user1)

[*]Output
<__main__.User object at 0x7fc47284fb20>

Let’s override the __str__ method:
[*]Snippet

class User:
pass

def __str__(self) -> str:
return "New user object"

user1 = User()
print(user1)

[*]Output
New user object

5. __new__: When a class is instantiated, __new__ method is called just before __init__ method. The __new__ method does the object creation while __init__ does initialization(i.e. setting attributes for the object). One particular use case of this method is when implementing singleton design pattern. It checks if an instance of the class does not exist before creating any other.
Singleton design pattern ensures that only one instance of a class exists.
[*]Snippet

class Singleton:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance

__new__(cls) is a class method. Link to explanation on methods
[*]Usage

s1 = Singleton()
s2 = Singleton()
s3 = Singleton()

print(s1)
print(s2)
print(s3)

[*]Output
<__main__.Singleton object at 0x7f611939fd30>
<__main__.Singleton object at 0x7f611939fd30>
<__main__.Singleton object at 0x7f611939fd30>

As we can see, s1,s2, and s3 points to the same object because the memory addresses(0x7f611939fd30) are the same. What you will be on your computer may be different but it will remain the same for all on them

There are many other special methods, commonly know as ‘dunders’, available in Python, these are just a few examples.

Thanks for reading and feel free to explore my video collection on YouTube for more educational content. Don’t forget to subscribe to the channel to stay updated with future releases.

--

--

Ridwan Yusuf

RidwanRay.com -I provide engineers with actionable tips and guides for immediate use