Magic methods are specialized methods, denoted by double underscores, that wield the power to transform the behavior of Python classes in fascinating ways. By delving into the realm of magic techniques, you’ll unlock the key to crafting classes that seamlessly integrate with Python’s core functionality, from arithmetic operations to string representations.
This article was published as a part of the Data Science Blogathon!
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 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)
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__ 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 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 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.
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,
the magic methods are implicitly invoked by python that performs the following operations instead of the ones mentioned above,
These implicit operations that are defined inside the magic methods return boolean values.
__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__ 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 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__ 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.
We hope this article helped you unerstand all about python’s magic methods. If you’re eager to delve deeper into data science and expand your mystical coding prowess, look no further than our blackbelt plus program. The course covers all the advanced data science topics, projects, assignments and more. Explore the program today!
A. Python magic methods are special methods with double underscores (e.g., init, str) that enable customization of class object behaviors.
A. The magic method in Python for division is __truediv__()
. This method is called when the division operator /
is used between two objects of a class.
A. To write a magic method, define it within the class, like def __add__(self, other):.
A. The magic call method in Python is __call__. It allows class instances to be called like functions, executing the code defined within that method.
The media shown in this article are not owned by Analytics Vidhya and are used at the Author’s discretion.