Picture by Writer

In Python, magic strategies provide help to 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 provide help to implement operator overloading in Python. You’ve in all probability seen examples of this. Like utilizing the multiplication operator * with two integers offers the product. Whereas utilizing it with a string and an integer `ok`

offers 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 conversant in and steadily construct as much as extra useful magic strategies.

Let’s begin writing some magic strategies!

Think about 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 a substitute of manually including attributes to each occasion that you simply create (not fascinating in any respect, in fact!), you want a strategy to initialize these attributes whenever you instantiate an object.

To take action you’ll be able to outline the `__init__`

technique. Let’s outline the outline the `__init__`

technique 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 thing you instantiated, you’ll see that you do not get any useful data.

```
v = Vector2D(3, 5)
print(v)
```

`Output >>> <__main__.Vector2D object at 0x7d2fcfaf0ac0>`

Because of this you must add a illustration string, a string illustration of the thing. To take action, add a `__repr__`

technique 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__`

technique is often used for the aim of debugging.

The `__str__`

can also be used so as to add a string illustration of the thing. Generally, the `__str__`

technique is used to offer data to the top customers of the category.

Let’s add a `__str__`

technique 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, you must—on the minimal—add a `__repr__`

technique.

Subsequent, let’s add a way to examine 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 examine 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 thing IDs in reminiscence.

Let’s add the `__eq__`

technique to examine 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 components that the vector incorporates.

So let’s add a `__len__`

technique 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 frequent operations we’d carry out on vectors. Let’s add magic strategies so as to add and subtract any two vectors.

In case you immediately attempt to add two vector objects, you’ll run into errors. So you must add an `__add__`

technique:

```
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__`

technique 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__`

technique to outline multiplication between objects.

Let’s implement let’s deal with

- Scalar multiplication: the multiplication of a vector by scalar and
- Inside 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:
elevate TypeError("Unsupported operand kind for *")
```

Now we’ll take a few examples to see the `__mul__`

technique 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 technique lets you 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 coordinate`v[1]`

: y coordinate

In case 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 is just not subscriptable |

Let’s implement the `__getitem__`

technique:

```
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:
elevate 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__`

technique, you’ll be able to name objects as in the event that they have been capabilities.

Within the `Vector2D`

class, we will 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 when 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__`

technique is used to get the values of particular attributes of the objects.

For this instance, we will add a `__getattr__`

dunder technique 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, identify):
if identify == "magnitude":
return (self.x ** 2 + self.y ** 2) ** 0.5
else:
elevate AttributeError(f"'Vector2D' object has no attribute '{identify}'")
```

Let’s confirm if this works as anticipated:

```
v = Vector2D(3, 4)
print(v.magnitude)
```

That is all for this tutorial! I hope you discovered methods to add magic strategies to your class to emulate the conduct of built-in capabilities.

We’ve lined among 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, knowledge science, and content material creation. Her areas of curiosity and experience embrace DevOps, knowledge science, and pure language processing. She enjoys studying, writing, coding, and occasional! Presently, she’s engaged on studying and sharing her data with the developer group by authoring tutorials, how-to guides, opinion items, and extra.