Demystifying Python OOP (Part 1) - Magic methods or Special methods

26 Aug 2018 10 mins read
python

In my previous post we talked about Python’s __getitem__ and __setitem__ methods. These methods are called magic methods or special methods or dunder methods. Well what is the magic about these methods? This is exactly what we are going to see today.

P.S: You will fall in love with Python language (again! ;) )

And it’s gonna be a long post. So, let’s get started.

What exactly are magic methods?

Magic methods or Dunder methods are just normal methods but with special powers. These magic methods in Python, help you to define the magic to your classes. These magic methods are defined by adding double underscores (__) as prefix and suffix to the method name. To be frank, there isn’t any magic going on. These methods are not well documented in the Python docs and hence we will be seeing these in detail today.

Magic methods for Initialization

All Python developers are initially taught that __init__ is the first method (or the constructor) that is called after a class is created. But __init__ is not the first method that is been called. Its actually the __new__ method that is been called initially.

Let’s look at an example:


class SimpleInit(object):
    '''
        Class to initialize a list with a value
    '''
    def __init__(self, value=10):
        self._list = [value]

    def __del__(self):
        del self._list

Magic Methods for Arithmetic Operations

Arithmetic operations are very common and their magic methods are really handy if you want to create your own data structures. For example, list in python concatenate if we do some_list + some_list2. Such kind of behaviour can be defined using magic methods for arithmetic operators.

Example:


class SimpleAdder(object):
    def __init__(self, elements=[]):
        self._list = elements
        
    def __add__(self, other):
        return self._list + other._list
    
    def __str__(self):
        return str(self._list)
    
a = SimpleAdder(elements=[1,2,3,4])
b = SimpleAdder(elements=[2, 3, 4])
print(a + b)    # [1, 2, 3, 4, 2, 3, 4]

Magic methods for Augmented assignment

Python not only allows us to define custom arithmeic operations but also provide the methods for augmented assignment too! For those who don’t know what augmented assignment is, let us see that with a simple example. Suppose

    x = 5
    x += 1              # This first adds 5 and 1 and then assigns it back to 'x'

Hence there might be a situation where you want to write some custom logic for augmented assignment operators. The supported operations for the same are:

Magic methods for Comparison

Python has an extensive set of magic methods for comparisons. We can override the default behaviour of comparison operators to make them work with object references. Here’s the list of comparison magic methods:

Example:


class WordCounter(object):
    '''
        Simple class to count number of words in a sentence
    '''
    def __init__(self, sentence):
        # split the sentence on ' '
        if type(sentence) != str:
            raise TypeError('The sentence should be of type str and not {}'.format(type(sentence)))
        self.sentence = sentence.split(' ')
        self.count    = len(self.sentence)
        
    def __eq__(self, other_class_name):
        '''
            Check the equality w.r.t length of the list with other class
        '''
        return self.count == other_class_name.count
    
    def __lt__(self, other_class_name):
        '''
            Check the less-than w.r.t length of the list with other class
        '''
        return self.count < other_class_name.count
    
    def __gt__(self, other_class_name):
        '''
            Check the greater-than w.r.t length of the list with other class
        '''
        return self.count > other_class_name.count
    
word = WordCounter('Omkar Pathak')
print(word.count)                   # 2
print(word == WordCounter("Omkar")) # False
print(word < WordCounter("Omkar"))  # False
print(word > WordCounter("Omkar"))  # True

Magic methods for type conversion

Many-a-times developers need to typecast their variables to grab the desired results. Python being a dynamically typed language takes care of your data types internally. But hey, Python cares for you too. If you want you can define custom behaviour while casting using these methods:

Most frequently used Magic Methods

These are some of the magic methods that you will come across frequently:


class CustomList(object):
    def __init__(self, elements=0):
        self.my_custom_list = [0] * elements

    def __str__(self):
        return str(self.my_custom_list)

    def __setitem__(self, index, value):
        self.my_custom_list[index] = value

    def __getitem__(self, index):
        return "Hey you are accessing {} element whose value is: {}".format(index, self.my_custom_list[index])

    def __iter__(self):
        return iter(self.my_custom_list)

obj = CustomList(12)
obj[0] = 1
print(obj[0])
print(obj)

So these where some of the magic methods that Python gifts you with. There are many many more and you should research about them as and when required. There is a similar blog post I came across when I was researching some of the magic methods, and I highly recommend all to read this post.

If anyone of you know more methods please do mention them in comments or you can directly open a Pull request here if you want to contribute :)


All content is licensed under the CC BY-SA 4.0 License unless otherwise specified