Magic Methods

Magic Methods are also known as Dunder methods, Special methods

Python dunder methods are the special predefined methods having two prefixes and two suffix underscores in the method name. Here, the word dunder means double under (underscore).

These special dunder methods are used in case of operator overloading (they provide extended meaning beyond the predefined meaning to an operator).

Some of the examples of most common dunder methods in use are __init__, __new__, __add__, __len__, __str__ method.

Use the dir(int) function to see the number of magic methods for integer

  • __add__
    • The __add__() method is called internally when you use the + operator to add two numbers.
print(10 + 20)
# 30

a = 10
print(a.__add__(20))
# 30

'Amrit '.__add__('Prasad')
# 'Amrit Prasad'
  • __init__()
    • The __init__() method is automatically invoked during the time of the creation of an instance of a class.
    • It is called a magic method because it is called automatically by Python.
    • __init__ is an Initializer, not a constructor, it is called by the constructor
    • __init__ is used to set values as soon as new object are created
class Pwskills:
    def __init__(self, name):
        self.name = name
pw1 = Pwskills('Amrit')
print(pw1.name)
# Amrit
  • __str__()
    • Its goal is to be readable
  • __repr__()
    • Its goal is to be unambiguous
  • By default, takes __str__ if absent then takes __repr__
class String:   
    def __init__(self, string): 
        self.string = string

s1 = String('Amrit')
print(s1)
# <__main__.String object at 0x7f6fbc0f8940>


class String:   
    def __init__(self, string): 
        self.string = string

    def __str__(self):
        return 'readable string'

    def __repr__(self):
        return 'unambiguous repr'

s1 = String('Amrit')
print(s1)
# readable string
  • __new__()
    • In Python, the __new__() magic method is implicitly called before the __init__() method.
    • The __new__() method returns a new object, which is then initialized by __init__() method.
class Employee:
    def __new__(cls):
        print ("__new__ magic method is called")
        inst = object.__new__(cls)
        return inst

    def __init__(self):
        print ("__init__ magic method is called")

emp = Employee()
# __new__ magic method is called
# __init__ magic method is called
  • Add two numbers using the + operator, internally, the __add__() method will be called.
class String:   
    def __init__(self, string): 
        self.string = string
    
    def __repr__(self): 
        return 'Object: {}'.format(self.string) 
        
    def __add__(self, other): 
        return self.string + other 

if __name__ == '__main__': 
    string1 = String('Hello')   
    print(string1 +' Geeks') 
class Employee(object):
    def __init__(self, firstname, lastname, salary=0):
        self.firstname = firstname
        self.lastname = lastname
        self.salary = salary

    # str() is used for creating output for end user
    def __str__(self):
        return 'Full Name: ' + self.firstname + ' ' + self.lastname

    # For overloading the (+)
    def __add__(self, other):
        return self.salary + other.salary

    # For overloading the (*)
    def __mul__(self, other):
        return self.salary * other.salary

    # repr() is mainly used for debugging and development
    def __repr__(self):
        return 'Object: {}'.format(self.firstname)


if __name__ == '__main__':
    e1 = Employee('Amrit', 'Prasad', 1000)
    e2 = Employee('Ravi', 'Prasad', 2000)

    print(e1)
    
    print(str(e1))      # OR
    # print(e1.__str__())

    print(repr(e1))     # OR
    # print(e1.__repr__())


    print(e1 + e2)
    # 3000 (This output because of __add__ method overloading)
    # print(Employee.__add__(e1,e2))

    print(e1 * e2)  # 2000000 (__mul__)
    # print(Employee.__mul__(e1,e2))

Reference