# Overloading

## Callable types

## 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.

Then, we can use a `sparselist`

much like a regular `list`

.

## 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.

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:

## 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.

Now it is possible to naturally use instances of the `Vector`

class in various expressions.

## 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 | `__matmul__(self, other)` | `a1 @ a2` (Python 3.5) |

`/` Division | `__div__(self, other)` | `a1 / a2` (Python 2 only) |

`/` Division | `__truediv__(self, other)` | `a1 / a2` (Python 3) |

`//` 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 than | `__gt__(self, other)` | `a1 > a2` |

`>=` Greater than or Equal to | `__ge__(self, other)` | `a1 >= a2` |

`[index]` Index operator | `__getitem__(self, index)` | `a1[index]` |

`in` In operator | `__contains__(self, other)` | `a2 in a1` |

`(*args, ...)` Calling | `__call__(self, *args, **kwargs)` | `a1(*args, **kwargs)` |

*The optional parameter modulo for __pow__ is 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__`

:

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

:

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 `int` | `__int__(self)` | `int(a1)` |

Absolute function | `__abs__(self)` | `abs(a1)` |

Casting to `str` | `__str__(self)` | `str(a1)` |

Casting to `unicode` | `__unicode__(self)` | `unicode(a1)` (Python 2 only) |

String representation | `__repr__(self)` | `repr(a1)` |

Casting to `bool` | `__nonzero__(self)` | `bool(a1)` |

String formatting | `__format__(self, formatstr)` | `"Hi {:abc}".format(a1)` |

Hashing | `__hash__(self)` | `hash(a1)` |

Length | `__len__(self)` | `len(a1)` |

Reversed | `__reversed__(self)` | `reversed(a1)` |

Floor | `__floor__(self)` | `math.floor(a1)` |

Ceiling | `__ceil__(self)` | `math.ceil(a1)` |

There are also the special methods `__enter__`

and `__exit__`

for context managers, and many more.