# Lesson 12 - Magic Methods in Python - Math methods

In the previous lesson, Magic Methods in Python, we introduced magic methods. In today's
Python tutorial, we're going to continue with them. This time, we're going to
show special methods for mathematical operators. We'll also show their use on
the `Vector`

class.

## Standard operators

These operators are called automatically on an object `a`

when
used like this:

c = a + b

The object `a`

is passed through the `self`

parameter
and the object `b`

is the `other`

parameter.

### __add__(self, other)

The method is called on the first object when the `+`

operator is
used:

c = a + b

### __sub__(self, other)

The method is called when subtracting by the `-`

operator:

c = a - b

### __mul__(self, other)

The method is called when multiplying by the `*`

operator:

c = a * b

### __truediv__(self, other)

The method is called when dividing by the `/`

operator:

c = a / b

### __floordiv__(self, other)

The method is called when dividing by the `//`

operator
(whole-number division):

c = a // b

### __mod__(self, other)

The method is called when getting the remainder after the whole-number
division by using the modulo operator `%`

:

c = a % b

### __divmod__(self, other)

Returns a pair `(a // b, a % b)`

for integers:

`c = divmod(a, b)`

### __pow__(self, other, modulo)

The method is executed when we use the power `**`

operator. The
method should be able to take a third, optional (modulo) argument (https://docs.python.org/â€¦nctions.html#pow):

c = a ** b

### __lshift__(self, other)

Executed when using the left bit-shift operator `<<`

:

c = a << b

### __rshift__(self, other)

Executed when using the right bit-shift operator `>>`

:

c = a >> b

### __and__(self, other)

Executed when using the bitwise AND operator `&`

:

c = a & b

### __xor__(self, other)

Executed when using the bitwise XOR operator `^`

(Non-Equivalence):

c = a ^ b

### __or__(self, other)

Executed when using the bitwise OR operator `|`

:

c = a | b

## Reversed operators

Reversed operators are called on the second object when the first object doesn't implement the operator.

For example:

`sth = 1 + my_object`

If the `__add__()`

magic method isn't supported by
`int`

, which is probably not, the `__radd__()`

method is
called on `my_object`

.

These operators are called automatically on the object `b`

, with
object `b`

being the `self`

parameter and object
`a`

being the other parameter.

`__radd__(self, other)`

- Adding`__rsub__(self, other)`

- Subtracting`__rmul__(self, other)`

- Multiplying`__rtruediv__(self, other)`

- True division`__rfloordiv__(self, other)`

- Whole-number division`__rmod__(self, other)`

- The remainder after whole-number division - modulo`__rdivmod__(self, other)`

- Returns a pair`(a // b, a % b)`

for integers`__rpow__(self, other, modulo)`

- Power`__rlshift__(self, other)`

- Left bit-shift`__rrshift__(self, other)`

- Right bit-shift`__rand__(self, other)`

- Bitwise AND`__rxor__(self, other)`

- Bitwise XOR (Non-Equivalence)`__ror__(self, other)`

- Bitwise OR

## In place operators

These operators allow us to use a shortened notation (in place). The
parameters of the methods are `self`

and `other`

, but
return a modified `self`

. If there's no method, Python will try to
emulate it using defined methods.

An example:

`my_object += 1`

Python calls the `__iadd__()`

method. If it fails, it calls
`__add__()`

as follows:

temp = my_object + 1 # calls __add__() my_object = temp

`__iadd__(self, other)`

- Adding`__isub__(self, other)`

- Subtracting`__imul__(self, other)`

- Multiplication`__itruediv__(self, other)`

- True division`__ifloordiv__(self, other)`

- Whole-number division`__imod__(self, other)`

The remainder of whole-number division - modulo`__ipow__(self, other, modulo)`

- Power`__ilshift__(self, other)`

- Left bit-shift`__irshift__(self, other)`

Right bit-shift`__iand__(self, other)`

- Bitwise AND`__ixor__(self, other)`

- Bitwise XOR (Non-Equivalence)`__ior__(self, other)`

- Bitwise OR

## Other magic methods

### __neg__(self)

Unary minus:

-a

### __pos__(self)

Unary plus:

+a

### __abs__(self)

Absolute value implementing behavior for `abs()`

:

`abs(a)`

### __invert__(self)

Unary inversion:

~a

### __complex__(self)

Implements behavior for the `complex()`

function:

`complex(a)`

### __int__(self)

Implements behavior for `int()`

:

`int(a)`

### __float__(self)

Implements behavior for `float()`

:

`float(a)`

### __round__(self, n)

Implements behavior for `round()`

:

`round(a)`

### __index__(self)

Python uses this method to convert numeric types to `int`

, for
example, when truncating or using the built-in `bin()`

,
`hex()`

, and `oct()`

functions. This method should return
the same result as the `__int__()`

magic method. It should also
return an integer (`int`

).

## Method examples

Let's create a simple `Vector`

class, holding its 2 `x`

and `y`

components. We'll implement some of the magic methods on
it.

class Vector: def __init__(self, x, y): self.x = float(x) self.y = float(y) def __str__(self): return "({0.x}, {0.y})".format(self) ...

The beginning is probably clear. In the `__str__()`

method, we get
the `x`

and `y`

attributes from `self`

.

def __add__(self, other): if isinstance(other, Vector): return Vector(self.x+other.x, self.y+other.y) elif issubclass(type(other), Sequence): if len(other) == 2: return Vector(self.x+other[0], self.y+other[1]) raise NotImplemented

First we compare whether the second object is also a `Vector`

. If
so, we return the sum of the `x`

and `y`

components as a
new `Vector`

.

Further branching is more complex. Using the built-in
`issubclass()`

function, we check if the second object class is a
subclass of `Sequence`

of the `collections.abc`

module.
This allows us to use the `len()`

function on the object to get the
first and second elements from the object. Without worrying that the object
wouldn't support it. If both branches fail and don't return anything, the
`NotImplemented`

exception is thrown.

def __mul__(self, other): if issubclass(type(other), Real): return Vector(self.x * other, self.y * other) raise NotImplemented

Here we check if `other`

is a subclass of `Real`

(real
number) from the `numbers`

module. Therefore, we can multiply the
`Vector`

by `int`

or `float`

and avoid
unnecessary branching and checking of object types.

*The classes in the numbers and collections.abc
modules are actually abstract base classes. These are classes where we have to
implement a certain object interface if we inherit them. But let's wait with
this for the next lesson, Magic Methods in Python - Collections and Descriptors.*

**Comments**

No one has commented yet - be the first!