Srivignesh Rajan — August 9, 2021
Beginner Programming Python

This article was published as a part of the Data Science Blogathon

Magic Methods

Magic methods are special methods in python that have double underscores (dunder) on both sides of the method name. Magic methods are predominantly used for operator overloading. Operator overloading means provided additional functionality to the operators, the python implicitly invokes the magic methods to provide additional functionality to it. For example, multiplying two integers can be done using the multiplying operator (2*3 = 6) and the same operator can be used to repeat the string (“apple- ” * 3 = ‘apple- apple- apple’).

Some examples of magic methods are __init__, __len__, __repr__, __add__, and etc.

__add__ magic method

__add__ magic method is used to add the attributes of the class instance. For example, let’s say object1 is an instance of a class A and object2 is an instance of class B and both of these classes have an attribute called ‘a’, that holds an integer. When the operation object1 + object2 is done the __add__ method implicitly adds the attributes of these instances like object1.a + object2.a, if defined so.

class One:
    def __init__(self, a):
        self.a = a
    def __add__(self, object2):
        return self.a + object2.a
class Two:
    def __init__(self, a):
        self.a = a
    def __add__(self, object2):
        return self.a + object2.a
a_instance = One(10)
b_instance = Two(20)
print(a_instance + b_instance)
# Output: 30

In the code above, classes One and Two possess an attribute called ‘a’ that holds an integer value. We define two instances a_instance and b_instances that contain 10 and 20 values in their attribute respectively. When we perform the operation a_instance + b_instance the __add__ method defined would convert the operation into a_instance.a + b_instance.a that results in output 30.

The exact operation could be done using strings.

class Str:
    def __init__(self, string_):
        self.string_ = string_
    def __add__(self, string2):
        return self.string_ + string2
instance1 = Str("Hello")
print(instance1 + " Folks")
# Output: Hello Folks

The operation instance1 + ” Folks” concatenates the two strings by implicitly performing the following operation instance1.string_ + ” Folks”.

__getitem__ and __setitem__ magic method

__getitem__ method is used to get an item from the invoked instances’ attribute. __getitem__ is commonly used with containers like list, tuple, etc.

class A:
    def __init__(self, item):
        self.item = item
    def __getitem__(self, index):
        return self.item[index]
a = A([1, 2, 3])
print(f"First item: {a[0]}")
print(f"Second item: {a[1]}")
print(f"Third item: {a[2]}")
# Output: 
# First item: 1
# Second item: 2
# Third item: 3

__setitem__ method is used to set the item into a specific index of the invoked instances’ attribute. Similar to __getitem__, __setitem__ is also used with containers.

class SetItemExample:
    def __init__(self, item):
        self.item = item
    def __setitem__(self, index, item1):
        self.item[index] = item1
setitem_instance = SetItemExample([1, 2, 3])
print(f"Items before setting: {setitem_instance.item}")
setitem_instance[1] = 5
print(f"Items after setting: {setitem_instance.item}")
# Output
# Items before setting: [1, 2, 3]
# Items after setting: [1, 5, 3]

__repr__ magic method

__repr__ magic method is used to represent the instance of a class in string. When we try to print the object of a class the __repr__ method is implicitly invoked that returns a string

class ReprExample:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    def __repr__(self):
        return f"ReprExample(a={self.a}, b={self.b}, c={self.c})"
repr_instance = ReprExample(1, 2, 3)
print(repr_instance)
# Output
# ReprExample(a=1, b=2, c=3)

The above class takes in three integer parameters namely a, b, and c. We create an instance by using ReprExample(1, 2, 3) and if we print the instance it prints the string defined in __repr__.

__len__ magic method

__len__ magic method is used to find the length of the instance attributes. When we use len(instance), it returns the length of the instance attribute which is usually containers.

class LenExample:
    def __init__(self, item):
        self.item = item
    def __len__(self):
        return len(self.item)
len_instance = LenExample([1, 2, 3])
print(len(len_instance))
# Output: 3

When we invoke the len() method the length of the list named item is returned that is defined inside the __len__ method.

__lt__, __gt__, __le__, __ge__, __eq__, and __ne__ magic methods

Comparative operators can be used to compare between the object’s attributes. The available methods are __lt__, __gt__, __le__, __ge__, __eq__, and __ne__ that performs less than, greater than, less than or equal to, greater than or equal to, equal to, and not equal to operations respectively.

class Comparison:
    def __init__(self, a):
        self.a = a
    def __lt__(self, object2):
        return self.a < object2.a
    def __gt__(self, object2):
        return self.a > object2.a
    def __le__(self, object2):
        return self.a <= object2.a
    def __ge__(self, object2):
        return self.a >= object2.a
    def __eq__(self, object2):
        return self.a == object2.a
    def __ne__(self, object2):
        return self.a != object2.a
a = Comparison(1)
b = Comparison(2)
print(
    a < b,
    a > b,
    a <= b,
    a >= b,
    a == b,
    a != b
)
# Output
# True False True False False True

The instances a and b contain an attribute named ‘a’ that holds integer values 1 and 2 respectively.

When we invoke the following operations,

  • a < b,
  • a > b,
  • a <= b,
  • a >= b,
  • a == b,
  • a != b

the magic methods are implicitly invoked by python that performs the following operations instead of the ones mentioned above,

  • a.a < b.a,
  • a.a > b.a,
  • a.a <= b.a,
  • a.a >= b.a,
  • a.a == b.a,
  • a.a != b.a

These implicit operations that are defined inside the magic methods return boolean values.

__contains__ magic method

__contains__ method is invoked when using the membership operator ‘in’. When we want to check whether an element is present in the object’s attributes that are usually container (lists, tuple) we can use the __contains__ method.

class ContainsExample:
    def __init__(self, items):
        self.items = items
    def __contains__(self, item):
        return item in self.items
contains_instance = ContainsExample([1, 2, 3, 4, 5])
print(4 in contains_instance)

The above code uses __contains__ method to find whether an integer is present in the list of integers. In the code above we checked whether the integer 4 is present in the list of integers that is an attribute of the class ContainsExample.

__enter__ and __exit__ magic methods

__enter__ and __exit__ methods are used along with the ‘with’ block in python. The ‘with’ block in python is used to open and close the file object or any other objects that have a close method.

class WriteFile:
    def __init__(self, file_name):
        self.file_name = file_name
        self.file = None
    def log(self, text):
        self.file.write(text+'n')
    def __enter__(self):
        self.file = open(self.file_name, "a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
with WriteFile(r"filename.txt") as log_file:
    log_file.log("Log Test 1")
    log_file.log("Log Test 2")

When the class is invoked with the file name ‘filename.txt’ the __enter__ method implicitly creates a text file. After executing the commands in the text file the __exit__ method is implicitly invoked to close the file object. In this way, we can use the __enter__ and __exit__ magic methods. These methods can also be used to open and close a database connection.

__call__ magic method

__call__ magic method is invoked when the instance of a class is invoked. Instead of writing another method to perform certain operations, we can use the __call__ method to directly call from the instance name.

class CallExample:
    def __init__(self, val):
        self.val = val
    def __call__(self, b):
        return self.val * b
call_example = CallExample(5)
print(call_example(6))
# Output: 30

The call_example instance can be directly called as a method since we have defined the __call__ method in the class. The __call__ method in this example gets in a value named b and multiplies with the ‘val’ attribute of the class that results in the output 30 in the above example.

__iter__ magic method

__iter__ method is used to provide a generator object for the provided instance. We can make use of iter() and next() method to leverage __iter__ method.

class Squares:
    def __init__(self, start, stop):
        self.start = start
        self.stop = stop
    def __iter__(self):
        for value in range(self.start, self.stop + 1):
            yield value ** 2
i = iter(Squares(1, 3))
print(next(i))
print(next(i))
print(next(i))
# Output: 1 4 9

In the code above we are going to find the squares of the values between the provided range (start and stop). When we invoke the method iter(Squares(1, 3)) the __iter__ method is invoked which is a method that yields squares of the values between the provided range. In our example, we use the range from 1 to 3 so the output would be 1, 4, and 9 if we invoke the next() method.

Summary

  • In this post, we have seen how to leverage the use of magic methods in python to provide additional functionalities to the class objects without any overhead.
  • The __add__ method is used to add two object’s attributes
  • The __getitem__ and __setitem__ methods are used to set and get the items of an object’s container attributes
  • The __repr__ method is used to print the object as a string
  • The __len__ method is used to find the length of the object’s attributes
  • __lt__, __gt__, __le__, __ge__, __eq__, and __ne__ methods are used for comparison of object’s attributes
  • __contains__ method is used for membership validation
  • __enter__ and __exit__ methods are used with the ‘with’ block in the python
  • __call__ method is used to use the object as a method.
  • __iter__ method is used to generate generator objects using the object

The media shown in this article are not owned by Analytics Vidhya and are used at the Author’s discretion.

About the Author

Our Top Authors

  • Analytics Vidhya
  • Guest Blog
  • Tavish Srivastava
  • Aishwarya Singh
  • Aniruddha Bhandari
  • Abhishek Sharma
  • Aarshay Jain

Download Analytics Vidhya App for the Latest blog/Article

Leave a Reply Your email address will not be published. Required fields are marked *