Picture by Creator
In Python, magic strategies assist you emulate the conduct of built-in capabilities in your Python courses. These strategies have main and trailing double underscores (__), and therefore are additionally referred to as dunder strategies.
These magic strategies additionally assist you implement operator overloading in Python. You’ve most likely seen examples of this. Like utilizing the multiplication operator * with two integers provides the product. Whereas utilizing it with a string and an integer ok
provides the string repeated ok
instances:
>>> 3 * 4
12
>>> 'code' * 3
'codecodecode'
On this article, we’ll discover magic strategies in Python by making a easy two-dimensional vector Vector2D
class.
We’ll begin with strategies you’re doubtless acquainted with and step by step construct as much as extra useful magic strategies.
Let’s begin writing some magic strategies!
Take into account the next Vector2D
class:
When you create a category and instantiate an object, you’ll be able to add attributes like so: obj_name.attribute_name = worth
.
Nevertheless, as an alternative of manually including attributes to each occasion that you simply create (not fascinating in any respect, in fact!), you want a approach to initialize these attributes whenever you instantiate an object.
To take action you’ll be able to outline the __init__
methodology. Let’s outline the outline the __init__
methodology for our Vector2D
class:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
v = Vector2D(3, 5)
While you attempt to examine or print out the item you instantiated, you’ll see that you do not get any useful info.
v = Vector2D(3, 5)
print(v)
Output >>> <__main__.Vector2D object at 0x7d2fcfaf0ac0>
Because of this it is best to add a illustration string, a string illustration of the item. To take action, add a __repr__
methodology like so:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
v = Vector2D(3, 5)
print(v)
Output >>> Vector2D(x=3, y=5)
The __repr__
ought to embrace all of the attributes and data wanted to create an occasion of the category. The __repr__
methodology is often used for the aim of debugging.
The __str__
can be used so as to add a string illustration of the item. Normally, the __str__
methodology is used to supply information to the tip customers of the category.
Let’s add a __str__
methodology to our class:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector2D(x={self.x}, y={self.y})"
v = Vector2D(3, 5)
print(v)
Output >>> Vector2D(x=3, y=5)
If there is no such thing as a implementation of __str__
, it falls again to __repr__
. So for each class that you simply create, it is best to—on the minimal—add a __repr__
methodology.
Subsequent, let’s add a technique to verify for equality of any two objects of the Vector2D
class. Two vector objects are equal if they’ve equivalent x and y coordinates.
Now create two Vector2D
objects with equal values for each x and y and evaluate them for equality:
v1 = Vector2D(3, 5)
v2 = Vector2D(3, 5)
print(v1 == v2)
The result’s False. As a result of by default the comparability checks for equality of the item IDs in reminiscence.
Let’s add the __eq__
methodology to verify for equality:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __eq__(self, different):
return self.x == different.x and self.y == different.y
The equality checks ought to now work as anticipated:
v1 = Vector2D(3, 5)
v2 = Vector2D(3, 5)
print(v1 == v2)
Python’s built-in len()
perform helps you compute the size of built-in iterables. Let’s say, for a vector, size ought to return the variety of parts that the vector comprises.
So let’s add a __len__
methodology for the Vector2D
class:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __len__(self):
return 2
v = Vector2D(3, 5)
print(len(v))
All objects of the Vector2D
class are of size 2:
Now let’s consider widespread operations we’d carry out on vectors. Let’s add magic strategies so as to add and subtract any two vectors.
When you instantly attempt to add two vector objects, you’ll run into errors. So it is best to add an __add__
methodology:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __add__(self, different):
return Vector2D(self.x + different.x, self.y + different.y)
Now you can add any two vectors like so:
v1 = Vector2D(3, 5)
v2 = Vector2D(1, 2)
outcome = v1 + v2
print(outcome)
Output >>> Vector2D(x=4, y=7)
Subsequent, let’s add a __sub__
methodology to calculate the distinction between any two objects of the Vector2D
class:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __sub__(self, different):
return Vector2D(self.x - different.x, self.y - different.y)
v1 = Vector2D(3, 5)
v2 = Vector2D(1, 2)
outcome = v1 - v2
print(outcome)
Output >>> Vector2D(x=2, y=3)
We are able to additionally outline a __mul__
methodology to outline multiplication between objects.
Let’s implement let’s deal with
- Scalar multiplication: the multiplication of a vector by scalar and
- Interior product: the dot product of two vectors
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __mul__(self, different):
# Scalar multiplication
if isinstance(different, (int, float)):
return Vector2D(self.x * different, self.y * different)
# Dot product
elif isinstance(different, Vector2D):
return self.x * different.x + self.y * different.y
else:
increase TypeError("Unsupported operand sort for *")
Now we’ll take a few examples to see the __mul__
methodology in motion.
v1 = Vector2D(3, 5)
v2 = Vector2D(1, 2)
# Scalar multiplication
result1 = v1 * 2
print(result1)
# Dot product
result2 = v1 * v2
print(result2)
Output >>>
Vector2D(x=6, y=10)
13
The __getitem__
magic methodology means that you can index into the objects and entry attributes or slice of attributes utilizing the acquainted square-bracket [ ] syntax.
For an object v
of the Vector2D
class:
v[0]
: x coordinatev[1]
: y coordinate
When you attempt accessing by index, you’ll run into errors:
v = Vector2D(3, 5)
print(v[0],v[1])
---------------------------------------------------------------------------
TypeError Traceback (most up-to-date name final)
in ()
----> 1 print(v[0],v[1])
TypeError: 'Vector2D' object isn't subscriptable |
Let’s implement the __getitem__
methodology:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __getitem__(self, key):
if key == 0:
return self.x
elif key == 1:
return self.y
else:
increase IndexError("Index out of vary")
Now you can entry the weather utilizing their indexes as proven:
v = Vector2D(3, 5)
print(v[0])
print(v[1])
With an implementation of the __call__
methodology, you’ll be able to name objects as in the event that they have been capabilities.
Within the Vector2D
class, we are able to implement a __call__
to scale a vector by a given issue:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __call__(self, scalar):
return Vector2D(self.x * scalar, self.y * scalar)
So should you now name 3, you’ll get the vector scaled by issue of three:
v = Vector2D(3, 5)
outcome = v(3)
print(outcome)
Output >>> Vector2D(x=9, y=15)
The __getattr__
methodology is used to get the values of particular attributes of the objects.
For this instance, we are able to add a __getattr__
dunder methodology that will get referred to as to compute the magnitude (L2-norm) of the vector:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __getattr__(self, title):
if title == "magnitude":
return (self.x ** 2 + self.y ** 2) ** 0.5
else:
increase AttributeError(f"'Vector2D' object has no attribute '{title}'")
Let’s confirm if this works as anticipated:
v = Vector2D(3, 4)
print(v.magnitude)
That is all for this tutorial! I hope you realized find out how to add magic strategies to your class to emulate the conduct of built-in capabilities.
We’ve lined a few of the most helpful magic strategies. However this isn’t this isn’t an exhaustive record. To additional your understanding, create a Python class of your alternative and add magic strategies relying on the performance required. Maintain coding!
Bala Priya C is a developer and technical author from India. She likes working on the intersection of math, programming, information science, and content material creation. Her areas of curiosity and experience embrace DevOps, information science, and pure language processing. She enjoys studying, writing, coding, and low! At the moment, she’s engaged on studying and sharing her information with the developer group by authoring tutorials, how-to guides, opinion items, and extra.