Explore the Magic Methods in 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.
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
Frequently Asked Questions
A. Magic methods in Python are used to define how objects of a class behave in response to certain operations, such as addition, comparison, indexing, and string representation.
The use of magic methods in Python provides several benefits, including customizing the behavior of objects, making code more readable, enhancing the functionality of your code, and coviding compatibility with built-in Python functions.
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.