Customising Operators in Python
24 Apr 2016Python classes allow the encapsulation of functionality in cleanly defined objects, and when used well can allow abstractions in usage that make for more understandable and readable code.
Consider the simple example of a class that defines a 3-vector, which I will
call Vector3
. It takes an x, y and z value and implements simple addition of
other vectors:
class Vector3(object):
"""
Describes a 3-vector.
"""
def __init__(self, x, y, z):
self.x = float(x)
self.y = float(y)
self.z = float(z)
def add(self, other):
"""
Compute the sum of this and another vector.
"""
# Sum the coordinates
x = self.x + other.x
y = self.y + other.y
z = self.z + other.z
# Create the new vector and return
return Vector3(x, y, z)
In code which manipulates vectors, we no longer need to manually write out the addition operations for each of the coordinates, but can simply add two vectors like:
vec1 = Vector3(1, 2, 2)
vec2 = Vector3(2, 1, 0)
newvec = vec1.add(vec2)
Defining Operators
The add
method above is a nice abstraction, however it is limited by the fact
that we need to know to remember the interface, and that the use of a method
call with the second object as an argument is a little clunky. A nice solution
is to make our class support the +
addition operator, so that the last line
of the above code snippet becomes simply:
newvec = vec1 + vec2
Python achieves this through the use of special methods, identifiable by
starting and ending with two underscores. You may have seen other special
methods like __init__
above, however some of these methods define the
behaviour when an operator is applied.
By default, trying to add two objects of a cusom class will result in a
TypeError
:
vec1 + vec2
# TypeError: unsupported operand type(s) for +: 'Vector3' and 'Vector3'
To support the addition operator, we need to define the
__add__
method with the signature:
def __add__(self, other):
Fortunately, this has the same signature as the add
method we defined above,
so simply renaming the method to __add__
will make additions of Vector3
objects work as intended.
Complete Implementation
Any of the Python operators can be overloaded in this way - see the documentation on the Python data model for a full listing, but the most commonly used are the numeric operators:
+
–__add__
-
–__sub__
*
–__mul__
/
–__truediv__
**
–__pow__
and the logical operators:
==
–__eq__
!=
–__ne__
<
–__lt__
>
–__gt__
<=
–__le__
>=
–__ge__
and
–__and__
or
–__or__
There are also the augmented assignment operators, that modify a variable
in-place, usually used for scaling or incrementing numeric values. These are
named with a leading ‘i’, like __iadd__
for the +=
operator.
Below is a more complete version of the Vector3
class from above,
demonstrating the implementation and usage of the normal and in-place additon
and subtraction operators. A __str__
special method has also been added to
provide decriptive printing of Vector3
objects when passed to the print
function.
class Vector3(object):
"""
Describes a 3-vector.
"""
def __init__(self, x, y, z):
self.x = float(x)
self.y = float(y)
self.z = float(z)
def __str__(self):
"""
Return a formatted string representation of the vector.
"""
return 'Vector3({}, {}, {})'.format(self.x, self.y, self.z)
def __add__(self, other):
"""
Compute the sum of this and another vector.
"""
# Sum the coordinates
x = self.x + other.x
y = self.y + other.y
z = self.z + other.z
# Create the new vector and return
return Vector3(x, y, z)
def __sub__(self, other):
"""
Compute the difference of this and another vector.
"""
# Subtract the coordinates
x = self.x - other.x
y = self.y - other.y
z = self.z - other.z
# Create the new vector and return
return Vector3(x, y, z)
def __iadd__(self, other):
"""
Add on another vector in-place.
"""
self.x += other.x
self.y += other.y
self.z += other.z
return self
def __isub__(self, other):
"""
Subtract another vector in-place.
"""
self.x -= other.x
self.y -= other.y
self.z -= other.z
return self
# Example Usage
# Define two initial vectors
vec1 = Vector3(1, 0, 0)
vec2 = Vector3(0, 1, 0)
# Add and print
print(vec1 + vec2)
# Vector3(1.0, 1.0, 0.0)
# Subtract and print
print(vec1 - vec2)
# Vector3(1.0, -1.0, 0.0)
# Add in-place
vec1 += vec2
print(vec1)
# Vector3(1.0, 1.0, 0.0)
# Subtract a new vector in-place
vec1 -= Vector3(0, 0, 1)
print(vec1)
# Vector3(1.0, 1.0, -1.0)