Python Object Oriented Programming | Magic Methods

Magic methods are special methods which have double underscores at the beginning and end of their names. They are known as dunders. They are used to create functionality that can’t be represented as a normal method.

One common use of them is operator overloading.
This means defining operators for custom classes that allow operators such as + and * to be used on them.

An example magic method is __add__ for +.

Example:

class Vector2D:
	def __init__(self, x, y):
		self.x = x
		self.y = y
	def __add__(self, other):
		return Vector2D(self.x + other.x, self.y + other.y)

first = Vector2D(5, 7)
second = Vector2D(3, 9)
result = first + second
print(result.x)
print(result.y)

The terminal output will be:

ddn_ro@linux:~/Desktop$ python file.py
8
16
ddn_ro@linux:~/Desktop$

The __add__ method allows for the definition of a custom behavior for the + operator in our class. As you can see, it adds the corresponding attributes of the objects and returns a new object, containing the result. Once it’s defined, we can add two objects of the class together.

More magic methods for common operators:

The __sub__ for -
The __mul__ for *
The __truediv__ for /
The __floordiv__ for //
The __mod__ for %
The __pow__ for **
The __and__ for &
The __xor__ for ^
The __or__ for |

The expression x + y is translated into x.__add__(y).
However, if x hasn’t implemented __add__, and x and y are of different types, then y.__radd__(x) is called.
There are equivalent r methods for all magic methods just mentioned.

Example:

class MagicString:
	def __init__(self, cont):
		self.cont = cont

	def __truediv__(self, other):
		sep_line = "-" * len(other.cont)
		return "\n".join([self.cont, sep_line, other.cont])

no_one = MagicString("I like Honda!")
no_two = MagicString("Others are good too!")
print(no_one / no_two)

The terminal output will be:

ddn_ro@linux:~/Desktop$ python3 file.py
I like Honda!
--------------------
Others are good too!
ddn_ro@linux:~/Desktop$

Python also provides magic methods for comparisons.

The __lt__ for <
The __le__ for <=
The __eq__ for ==
The __ne__ for !=
The __gt__ for >
The __ge__ for >=

If __ne__ is not implemented, it returns the opposite of __eq__.
There are no other relationships between the other operators.

Example:

class SpecialString:
	def __init__(self, cont):
		self.cont = cont

	def __gt__(self, other):
		for index in range(len(other.cont)+1):
			result = other.cont[:index] + ">" + self.cont
			result += ">" + other.cont[index:]
			print(result)

yamaha = SpecialString("yamaha")
suzuki = SpecialString("suzuki")
yamaha > suzuki

The terminal output will be:

ddn_ro@linux:~/Desktop$ python file.py
>yamaha>suzuki
s>yamaha>uzuki
su>yamaha>zuki
suz>yamaha>uki
suzu>yamaha>ki
suzuk>yamaha>i
suzuki>yamaha>
ddn_ro@linux:~/Desktop$

As you can see, you can define any custom behavior for the overloaded operators.

There are several magic methods for making classes act like containers.

The __len__ for len()
The __getitem__ for indexing
The __setitem__ for assigning to indexed values
The __delitem__ for deleting indexed values
The __iter__ for iteration over objects
The __contains__ for in

Other magic methods:

The __call__ for calling objects as functions
The __int__
The __str__

Example:

import random

class VagueList:
	def __init__(self, cont):
		self.cont = cont

	def __getitem__(self, index):
		return self.cont[index + random.randint(-1, 1)]

	def __len__(self):
		return random.randint(0, len(self.cont)*2)

vague_list = VagueList(["A", "B", "C", "D", "E"])

print(len(vague_list))
print(len(vague_list))
print(vague_list[2])
print(vague_list[2])

The terminal output will be:

ddn_ro@linux:~/Desktop$ python3 file.py
7
8
C
D
ddn_ro@linux:~/Desktop$

We have overridden the len() function for the class VagueList to return a random number. The indexing function also returns a random item in a range from the list, based on the expression.

Leave a Reply