Some operators and other elements of the Python programming language can show overloading. Here is all you need to know about Python Overloading.

## Python overloading: Operator overloading

Below are the operators that can be overloaded in classes, along with the method definitions that are required, and an example of the operator in use within an expression.

N.B. The use of other as a variable name is not mandatory, but is considered the norm.

```
Operator Method Expression
Addition
```

**add**(self, other) a1 + a2

Subtraction

**sub**(self, other) a1 – a2

Multiplication

mul(self, other) a1 * a2

Matrix Multiplication (Python 3.5)

@matmul(self, other)

a1 @ a2

Division (Python 2 only)

/div(self, other)

a1 / a2

Division (Python 3)

/truediv(self, other)

a1 / a2

Floor Division

//floordiv(self, other) a1 // a2

Modulo/Remainder

%mod(self, other) a1 % a2

Power

**pow(self, other[, modulo]) a1 ** a2

Bitwise Left Shift

<<lshift(self, other) a1 << a2

Bitwise Right Shift

```
__rshift__(self, other) a1 >> a2
Bitwise AND
```

&and(self, other) a1 & a2

Bitwise XOR

^xor(self, other) a1 ^ a2

(Bitwise OR)

|or(self, other) a1 | a2

Negation (Arithmetic)

**neg**(self) -a1

Positive

pos(self) +a1

Bitwise NOT

~invert(self) ~a1

Less than

<lt(self, other) a1 < a2 Less than or Equal to <=le(self, other) a1 <= a2 Equal to ==eq(self, other) a1 == a2 Not Equal to !=ne(self, other) a1 != a2 Greater thangt(self, other) a1 > a2

Greater than or Equal to

=ge(self, other) a1 >= a2

Index operator

[index]getitem(self, index) a1[index]

In operator

incontains(self, other) a2 in a1

Calling

(*args, …)call(self, *args, *kwargs) a1(args, **kwargs)

The optional parameter modulo forpowis only used by the pow built-in function.

Each of the methods corresponding to a binary operator has a corresponding “right” method which start with **r, for example __radd**:

class A: definit(self, a): self.a = a defadd(self, other): return self.a + other defradd(self, other): print("radd")

return other + self.a

A(1) + 2 # Out: 3

2 + A(1) # prints radd. Out: 3

as well as a corresponding inplace version, starting with __i:

class B:

definit(self, b):

self.b = b

defiadd(self, other):

self.b += other

print("iadd")

return self

b = B(2)

#### Out: 2

b += 1 # prints iadd

#### Out: 3

Since there’s nothing special about these methods, many other parts of the language, parts of the standard library, and even third-party modules add magic methods on their own, like methods to cast an object to a type or checking properties of the object. For example, the builtin str() function calls the object’s **str** method, if it exists. Some of these uses are listed below.

Function Method Expression

Casting to

intint(self) int(a1)

Absolute functionabs(self) abs(a1)

Casting to

strstr(self) str(a1)

Casting to (Python 2 only)unicode(self)

unicode unicode(a1)

String representationrepr(self) repr(a1)

Casting to

boolnonzero(self) bool(a1)

String formattingformat(self, formatstr) "Hi {:abc}".format(a1)

Hashinghash(self) hash(a1)

Lengthlen(self) len(a1)

Reversedreversed(self) reversed(a1)

Floorfloor(self) math.floor(a1)

Ceilingceil(self) math.ceil(a1)

There are also the special methodsenterandexitfor context managers, and many more.

## Python overloading: Magic/Dunder Methods

Magic (also called dunder as an abbreviation for double-underscore) methods in Python serve a similar purpose to operator overloading in other languages. They allow a class to define its behavior when it is used as an operand in unary or binary operator expressions. They also serve as implementations called by some built-in functions.

Consider this implementation of two-dimensional vectors.

import math

class Vector(object):

#### instantiation

definit(self, x, y):

self.x = x

self.y = y

unary negation (-v) defneg(self):

return Vector(-self.x, -self.y)

addition (v + u)

defadd(self, other):

return Vector(self.x + other.x, self.y + other.y)

#### subtraction (v – u)

defsub(self, other):

return self + (-other)

#### equality (v == u)

defeq(self, other):

return self.x == other.x and self.y == other.y

#### abs(v)

defabs(self):

return math.hypot(self.x, self.y)

#### str(v)

defstr(self):

return '<{0.x}, {0.y}>'.format(self)

#### repr(v)

defrepr(self):

return 'Vector({0.x}, {0.y})'.format(self)

Now it is possible to naturally use instances of the Vector class in various expressions.

v = Vector(1, 4)

u = Vector(2, 0)

u + v # Vector(3, 4)

print(u + v) # "<3, 4>" (implicit string conversion)

u - v # Vector(1, -4)

u == v # False

u + v == v + u # True

abs(u + v) # 5.0

## Python overloading: Container and sequence types

It is possible to emulate container types, which support accessing values by key or index.

Consider this naive implementation of a sparse list, which stores only its non-zero elements to conserve memory.

class sparselist(object):

definit(self, size):

self.size = size

self.data = {}

##### l[index]

defgetitem(self, index): if index < 0: index += self.size if index >= self.size: raise IndexError(index)

try:

return self.data[index]

except KeyError:

return 0.0

#### l[index] = value

defsetitem(self, index, value):

self.data[index] = value

#### del l[index]

defdelitem(self, index):

if index in self.data:

del self.data[index]

#### value in l

defcontains(self, value):

return value == 0.0 or value in self.data.values()

#### len(l)

deflen(self):

return self.size

for value in l: …

defiter(self):

return (self[i] for i in range(self.size)) # use xrange for python2

Then, we can use a sparselist much like a regular list.

l = sparselist(10 ** 6) # list with 1 million elements

0 in l # True

10 in l # False

l[12345] = 10

10 in l # True

l[12345] # 10

for v in l:

pass # 0, 0, 0, … 10, 0, 0 … 0

## Callable types

class adder(object):

definit(self, first):

self.first = first

### a(…)

defcall(self, second):

return self.first + second

add2 = adder(2)

add2(1) # 3

add2(2) # 4

## Python overloading: Handling unimplemented behaviour

If your class doesn’t implement a specific overloaded operator for the argument types provided, it should return NotImplemented (note that this is a special constant, not the same as NotImplementedError). This will allow Python to fall back to trying other methods to make the operation work:

When NotImplemented is returned, the interpreter will then try the reflected operation on the other type, or some other fallback, depending on the operator. If all attempted operations return NotImplemented, the interpreter will raise an appropriate exception.

For example, given x + y, if x.**add**(y) returns unimplemented, y.**radd**(x) is attempted instead.

class NotAddable(object):

definit(self, value):

self.value = value

defadd(self, other):

return NotImplemented

class Addable(NotAddable):

defadd(self, other):

return Addable(self.value + other.value)radd=add

As this is the reflected method we have to implement **add** and **radd** to get the expected behaviour in all cases; fortunately, as they are both doing the same thing in this simple example, we can take a shortcut.

In use:

x = NotAddable(1)

y = Addable(2)

x + x

Traceback (most recent call last):

File "", line 1, in

TypeError: unsupported operand type(s) for +: 'NotAddable' and 'NotAddable'

y + y

z = x + y

z

z.value

3

### Must Read Python Interview Questions

### 200+ Python Tutorials With Coding Examples

#### Other Python Tutorials

- What is Python?
- Python Advantages
- Python For Beginners
- Python For Machine Learning
- Machine Learning For Beginners
- 130+ Python Projects With Source Code On GitHub