This chapter is taken from the book A Primer on Scientific Programming with Python by H. P. Langtangen, 5th edition, Springer, 2016.

This section explains how to implement two-dimensional vectors in Python such that these vectors act as objects we can add, subtract, form inner products with, and do other mathematical operations on. To understand the forthcoming material, it is necessary to have digested the section Special methods, in particular the sections Adding objects and Arithmetic operations and other special methods.

Vectors in the plane are described by a pair of real numbers, \( (a, b) \). There are mathematical rules for adding and subtracting vectors, multiplying two vectors (the inner or dot or scalar product), the length of a vector, and multiplication by a scalar: $$ \begin{align} (a,b) + (c,d) &= (a+c, b+d), \tag{4}\\ (a,b) - (c,d) &= (a-c, b-d), \tag{5}\\ (a,b)\cdot(c,d) &= ac + bd, \tag{6}\\ ||(a,b)|| &= \sqrt{(a,b)\cdot(a,b)} \thinspace . \tag{7} \end{align} $$ Moreover, two vectors \( (a,b) \) and \( (c,d) \) are equal if \( a=c \) and \( b=d \).

We may create a class for plane vectors where the above mathematical
operations are implemented by special methods. The class must contain
two data attributes, one for each component of the vector, called `x`

and
`y`

below. We include special methods for addition, subtraction, the scalar
product (multiplication), the absolute value (length), comparison of
two vectors (`==`

and `!=`

), as well as a method for printing out
a vector.

```
class Vec2D(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vec2D(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vec2D(self.x - other.x, self.y - other.y)
def __mul__(self, other):
return self.x*other.x + self.y*other.y
def __abs__(self):
return math.sqrt(self.x**2 + self.y**2)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __str__(self):
return '(%g, %g)' % (self.x, self.y)
def __ne__(self, other):
return not self.__eq__(other) # reuse __eq__
```

The `__add__`

,
`__sub__`

,
`__mul__`

,
`__abs__`

, and
`__eq__`

methods
should be quite straightforward to understand from the previous
mathematical definitions of these operations.
The last method deserves a comment: here we simply reuse the
equality operator `__eq__`

, but precede it with a `not`

.
We could also have implemented this method as

```
def __ne__(self, other):
return self.x != other.x or self.y != other.y
```

Nevertheless, this implementation requires us to write more, and it has
the danger of introducing an error in the logics of the boolean
expressions. A more reliable approach, when we know that the
`__eq__`

method works, is to reuse this method and
observe that `a != b`

means `not (a == b)`

.

A word of warning is in place regarding our implementation of the
equality operator (`==`

via `__eq__`

).
We test for equality of each component, which is correct from a
mathematical point of view. However, each vector component is a
floating-point number that may be subject to rounding errors both
in the representation on the computer and from previous (inexact)
floating-point calculations. Two mathematically equal components may
be different in their inexact representations on the computer.
The remedy for this problem is to avoid testing for equality, but instead
check that the difference between the components is sufficiently small.
The function `numpy.allclose`

can be used for this purpose:

```
if a == b:
```

by

```
if numpy.allclose(a, b):
```

A more reliable equality operator can now be implemented:

```
class Vec2D(object):
...
def __eq__(self, other):
return numpy.allclose(self.x, other.x) and \
numpy.allclose(self.y, other.y)
```

As a rule of thumb, you should never apply the `==`

test to two
`float`

objects.

The special method
`__len__`

could be introduced as a synonym for
`__abs__`

, i.e., for a `Vec2D`

instance named `v`

,
`len(v)`

is the same as `abs(v)`

, because the absolute value of
a vector is mathematically the same as the length of the vector.
However, if we implement

```
def __len__(self):
# Reuse implementation of __abs__
return abs(self) # equiv. to self.__abs__()
```

we will run into trouble when we compute `len(v)`

and the answer
is (as usual) a `float`

. Python will then complain and tell us that
`len(v)`

must return an `int`

. Therefore,
`__len__`

cannot be used as a synonym for the length of the
vector in our application. On the other hand, we could let
`len(v)`

mean the number of components in the vector:

```
def __len__(self):
return 2
```

This is not a very useful function, though, as we already know that all our
`Vec2D`

vectors have just two components.
For generalizations of the class to vectors with \( n \) components, the
`__len__`

method is of course useful.

Let us play with some `Vec2D`

objects:

```
>>> u = Vec2D(0,1)
>>> v = Vec2D(1,0)
>>> w = Vec2D(1,1)
>>> a = u + v
>>> print a
(1, 1)
>>> a == w
True
>>> a = u - v
>>> print a
(-1, 1)
>>> a = u*v
>>> print a
0
>>> print abs(u)
1.0
>>> u == v
False
>>> u != v
True
```

When you read through this interactive session, you should check
that the calculation is mathematically correct,
that the resulting object type of a calculation is correct,
and how each calculation is performed in the program.
The latter topic is investigated by following the program
flow through the class methods.
As an example, let us consider the expression `u != v`

.
This is a boolean expression that is `True`

since `u`

and `v`

are different vectors. The resulting object type should be `bool`

,
with values `True`

or `False`

. This is confirmed by the output
in the interactive session above. The Python calculation of
`u != v`

leads to a call to

```
u.__ne__(v)
```

which leads to a call to

```
u.__eq__(v)
```

The result of this last call is `False`

, because the special method
will evaluate the boolean expression

```
0 == 1 and 1 == 0
```

which is obviously `False`

. When going back to the `__ne__`

method, we end up with a return of `not False`

, which evaluates
to `True`

.

For real computations with vectors in the plane, you would probably just
use a Numerical Python array of length 2. However, one thing such objects
cannot do is evaluating `u*v`

as a scalar product.
The multiplication operator for Numerical Python arrays is not defined
as a scalar product (it is rather defined as
\( (a,b)\cdot(c,d) = (ac, bd) \)). Another difference between our `Vec2D`

class and Numerical Python arrays is the `abs`

function, which
computes the length of the vector in class `Vec2D`

, while it does something
completely different with Numerical Python arrays.