Python cheatsheet: operator overloading

Jérôme DIAZ
7 min readMar 6, 2024

--

Photo by Kevin Canlas on Unsplash

Operator overloading refers to the ability of using some built-in operators like ‘+’, ‘*’, … with class instances.

Some times you might want to use operator overloading to render your code more readable and less verbose.

This article is here to list the operators you can overload on classes.

Two operand operators are usually defined on the class of the left side operand, but Python propose in most cases (if the two operand aren’t instances of the same class) a fallback method to define on the right side operand class.

Arithmetic operators

Arithmetic operators are commonly used with numeric values for mathematical operations.

  • Addition: “x + y” “x += y”
x + y

def __add__(self, other):
# self is x, other is y

# if not defined, python will try to use __radd__ on y
# y.__radd__(x)

# should return a value


x += y

def __iadd__(self, other):
# self is x, other is y

# if not defined, python will try to use __add__ (which can fallback to __radd__)
# x = x.__add__(y)
# x = y.__radd__(x)

# should return a value, else x will become None
  • Subtraction: “x - y”, “x -= y”
x - y

def __sub__(self, other):
# self is x, other is y

# if not defined, python will try to use __rsub__ on y
# y.__rsub__(x)

# should return a value


x -= y
def __isub__(self, other):
# self is x, other is y

# if not defined, python will try to use __sub__ (which can fallback to __rsub__)
# x = x.__sub__(y)
# x = y.__rsub__(x)

# should return a value, else x will become None
  • Multiplication: “x * y”, “x *= y”
x * y

def __mul__(self, other):
# self is x, other is y

# if not defined, python will try to use __rmul__ on y
# y.__rmul__(x)

# should return a value


x *= y

def __imul__(self, other):
# self is x, other is y

# if not defined, python will try to use __mul__ (which can fallback to __rmul__)
# x = x.__mul__(y)
# x = y.__rmul__(x)

# should return a value, else x will become None
  • Division / true division: “x / y”, “x /= y”
x / y

def __truediv__(self, other):
# self is x, other is y

# if not defined, python will try to use __rtruediv__ on y
# y.__rtruediv__(x)

# should return a value


x /= y

def __itruediv__(self, other):
# self is x, other is y

# if not defined, python will try to use __truediv__ (which can fallback to __rtruediv__)
# x = x.__truediv__(y)
# x = y.__rtruediv__(x)

# should return a value, else x will become None
  • Floor division: “x // y”, “x //= y”
x // y

def __floordiv__(self, other):
# self is x, other is y

# if not defined, python will try to use __rfloordiv__ on y
# y.__rfloordiv__(x)

# should return a value


x //= y

def __ifloordiv__(self, other):
# self is x, other is y

# if not defined, python will try to use __floordiv__ (which can fallback to __rfloordiv__)
# x = x.__floordiv__(y)
# x = y.__rfloordiv__(x)

# should return a value, else x will become None
  • Modulus: “x % y”, “x %= y”
x % y

def __mod__(self, other):
# self is x, other is y

# if not defined, python will try to use __rmod__ on y
# y.__rmod__(x)

# should return a value


x %= y

def __imod__(self, other):
# self is x, other is y

# if not defined, python will try to use __mod__ (which can fallback to __rmod__)
# x = x.__mod__(y)
# x = y.__rmod__(x)

# should return a value, else x will become None
  • Exponentation/power: “x ** y”, “x **=y ”
x ** y

def __pow__(self, other):
# self is x, other is y

# if not defined, python will try to use __rpow__ on y
# y.__rpow__(x)

# should return a value


x **= y

def __ipow__(self, other):
# self is x, other is y

# if not defined, python will try to use __pow__ (which can fallback to __rpow__)
# x = x.__pow__(y)
# x = y.__rpow__(x)

# should return a value, else x will become None
  • Matrice multiplication: “x @ y”, “x @= y”
x @ y

def __matmul__(self, other):
# self is x, other is y

# if not defined, python will try to use __rmatmul__ on y
# y.__rmatmul__(x)

# should return a value


x @= y

def __imatmul__(self, other):
# self is x, other is y

# if not defined, python will try to use __matmul__ (which can fallback to __rmatmul__)
# x = x.__matmul__(y)
# x = y.__rmatmul__(x)

# should return a value, else x will become None
  • Negative: “-x”
-x

def __neg__(self) -> Self:
# self is x

# should by convention return a value of the same type than the instance
# and respect the equality
# x == -(-x)

Equality operators

Equality operators are used to check if two objects are equivalent or not

  • Equality: “x == y”
x == y

def __eq__(self, other):
# self is x, other is y

# if it returns NotImplemented, python will try to use __eq__ on y
# y.__eq__(x)

# should return a value, in most cases a boolean

  • Not equal: “x != y”, “x <> y”
x != y
x <> y

def __ne__(self, other):
# self is x, other is y

# if not defined, python will try to use the __eq__ implementation and reverse the result
# not x.__eq__(y): not (x == y)

# should return a value, in most cases a boolean

Comparison operators

Comparison operators are used to compare objects

  • Greater than: “x > y”, “x >= y”
x > y 

def __gt__(self, other):
# self is x, other is y

# if not defined, will try with y.__lt__(x): y < x

# should return a value, in most cases a boolean


x >= y

def __ge__(self, other):
# self is x, other is y

# if not defined, will try with y.__le__(x): y <= x

# should return a value, in most cases a boolean
  • Less than: “x < y”, “x <= y”
x < y 

def __lt__(self, other):
# self is x, other is y

# if not defined, will try with y.__gt__(x): y > x

# should return a value, in most cases a boolean


x <= y

def __le__(self, other):
# self is x, other is y

# if not defined, will try with y.__ge__(x): y >= x

# should return a value, in most cases a boolean

Bitwise operators

  • AND: “x & y”, “x &= y”
x & y

def __and__(self, other):
# self is x, other is y

# if not defined, python will try to use __rand__ on y
# y.__rand__(x)

# should return a value


x &= y

def __iand__(self, other):
# self is x, other is y

# if not defined, python will try to use __and__ (which can fallback to __rand__)
# x = x.__and__(y)
# x = y.__rand__(x)

# should return a value (else x will become None)
  • OR: “x | y”, “x |= y”
x | y

def __or__(self, other):
# self is x, other is y

# if not defined, python will try to use __ror__ on y
# y.__rand__(x)

# should return a value


x |= y

def __ior__(self, other):
# self is x, other is y

# if not defined, python will try to use __or__ (which can fallback to __radd__)
# x = x.__or__(y)
# x = y.__ror__(x)

# should return a value
  • Exclusive OR (XOR): “x ^ y”, “x ^= y”
x ^ y

def __xor__(self, other):
# self is x, other is y

# if not defined, python will try to use __rxor__ on y
# y.__rxor__(x)

# should return a boolean


x ^= y

def __ixor__(self, other):
# self is x, other is y

# if not defined, python will try to use __xor__ (which can fallback to __rxor__)
# x = x.__xor__(y)
# x = y.__rxor__(x)

# should return a boolean
  • Right Shift: “x >> y”, “x >>= y”
x >> y

def __rshift__(self, other):
# self is x, other is y

# if not defined, python will try to use __rrshift__ on y
# y.__rrshift__(x)

# should return a value


x >>= y

def __irshift__(self, other):
# self is x, other is y

# if not defined, python will try to use __rshift__ (which can fallback to __rrshift__)
# x = x.__rshift__(y)
# x = y.__rrshift__(x)

# should return a value
  • Left Shift: “x << y”, “x <<= y”
x << y

def __lshift__(self, other):
# self is x, other is y

# if not defined, python will try to use __rlshift__ on y
# y.__rlshift__(x)

# should return a value


x <<= y

def __ilshift__(self, other):
# self is x, other is y

# if not defined, python will try to use __lshift__ (which can fallback to __rlshift__)
# x = x.__lshift__(y)
# x = y.__rlshift__(x)

# should return a value
  • Inverse: “~x”
~x

def __invert__(self) -> Self:
# self is x

# there is no fallback for this one as it only as one operand

# should return a value of the same type than the instance

Subscript operators

Subscript operators are all operators using brackets to work with the inner content of an object like an element in a list or in a dictionary

x[y]

def __getitem__(self, key):
# self is x, key is y, key isn't necessary an index or a string key
# this method should return a value or raise an exception


x[y] = z

def __setitem__(self, key, value):
# self is x, key is y, key isn't necessary an index or a string key


del x[y]

def __delitem__(self, key):
# self is x, key is y, key isn't necessary an index or a string key

Other operators

  • Call operator

This operator allow to use an instance as a function.

x()

def __call__(self[, args...]):
# self is x
# there can be no arguments, positional arguments and or keyword arguments
# can return a value
  • Contains operator: “x in y”

This operator allow to use the “in” keyword

x in y

def __call__(self, element) -> bool:
# WARNING: self is y, element is x, arguments are reversed
# should return a boolean

That’s all! (If I haven’t forgotten any)

I hope this article will be as useful to you that it will be for me. (I started it as a cheatsheet for myself and learned many things while writing it)

--

--

Jérôme DIAZ

Software designer with two master's degrees and a natural curiosity. I enjoy understanding how things work: to fully use them and also create new solutions.