Implement 3D vector using dunder methods

In this tutorial, we will learn how to implement a 3D vector using Dunder methods in Python.

First, we will look at what dunder methods are.
Next, we look at the basic properties of a 3D vector.
Finally, we will implement a 3D vector class with dunder methods in Python.

Dunder methods in Python

The word ‘dunder’ comes from joining the words ‘double’ and ‘underscore’. Dunder methods are those methods of a class which have names beginning and ending with a double underscore (__). They help us implement certain functionalities to objects of a class that are similar to existing datatypes.

Consider this simple example. Although the ‘+’ (binary addition) operator generally refers to the addition of numeric types, Python allows it to be used for the concatenation of strings. This is done with the help of a dunder method called ‘__add__’.

Click here for a more comprehensive understanding of these methods.

Properties of 3D vectors

We wish to implement the following simple properties of vectors.

  1. Firstly, we wish to be able to initialise an object with 3 components. We use the ‘__init__’ dunder method to do so.
  2. Next, we wish to represent the vector as some ‘ai + bj + ck‘. We use the ‘__repr__’ dunder method to do this. This helps us to format the way the vector is printed.
  3. We define a function to display the magnitude of the vector. This is not a dunder method.
  4. We implement a method to work with the negative of a vector. We use the ‘__neg__’ dunder method to do so.
  5. For addition and subtraction of vectors, we use the help of the ‘__add__’ and ‘__sub__’ dunder methods.
  6. Multiplication in vectors is a little more complex. We overload the ‘*’ operator to have two meanings. We can use it for scalar multiplication as well as the dot product of two vectors. The dunder methods we use in this regard are ‘__mul__’ and ‘__rmul__’.
  7. Since a vector can also be divided by a scalar, we implement this with the ‘__truediv__’ dunder method. (This is to work with the ‘/’ operator).
  8. Finally, we implement the cross product of 2 vectors. I decided to use the ‘**’ operator as the symbol to denote cross product. The dunder method for this is ‘__pow__’.

We require a good understanding of operator overloading in Python to implement this program.

Implementation in Python: 3d vector

We implement the concepts so far in the following Python code.

# We define a class vector to handle vector objects
class vector:
  
  # For initialising the vector
  def __init__(self, x_comp = None, y_comp = None, z_comp = None):
    self.x_comp = x_comp
    self.y_comp = y_comp
    self.z_comp = z_comp

  # Representing the vector
  # Used to print a valid string
  def __repr__ (self):
    return '{}i {} {}j {} {}k'.format(self.x_comp, 
                                      '+' if self.y_comp >= 0 else '-', 
                                      abs(self.y_comp), 
                                      '+' if self.z_comp >= 0 else '-', 
                                      abs(self.z_comp))

  # Magnitude of the vector
  def mag(self):
    return ((self.x_comp ** 2 + self.y_comp ** 2 + self.z_comp ** 2) 
            ** 0.5)
  
  # Negative of a vector
  def __neg__(self):
    return (vector(-self.x_comp, -self.y_comp, -self.z_comp))

  # Addition of 2 vectors
  def __add__(first, second):
    
    return (vector(first.x_comp + second.x_comp, 
                   first.y_comp + second.y_comp, 
                   first.z_comp + second.z_comp))
  
  # Subtraction of 2 vectors
  def __sub__(first, second):
    
    return (vector(first.x_comp - second.x_comp, 
                  first.y_comp - second.y_comp, 
                  first.z_comp - second.z_comp))
  
  # We use '*' for both scalar multiplication
  # as well as dot product
  def __mul__(first, second):

    if (isinstance(second, (int, float))):
      return (vector(second * first.x_comp, 
                     second * first.y_comp, 
                     second * first.z_comp))

    else:
      return (first.x_comp * second.x_comp + 
              first.y_comp * second.y_comp + 
              first.z_comp * second.z_comp)

  def __rmul__(second, first):
    return (vector(first * second.x_comp, 
                   first * second.y_comp, 
                   first * second.z_comp))
  
  # Scalar division
  def __truediv__(first, second):
    return vector(first.x_comp / second, 
                  first.y_comp / second, 
                  first.z_comp / second)

  # We use '**' for cross product
  def __pow__(first, second):
    return vector(first.y_comp * second.z_comp - 
                  first.z_comp * second.y_comp, 
                  first.z_comp * second.x_comp - 
                  first.x_comp * second.z_comp, 
                  first.x_comp * second.y_comp - 
                  first.y_comp * second.x_comp) 

  
if __name__ == "__main__":

  # Creating a vector and printing it
  v = vector(-2, 3, -7)
  print(v)

  # Print magnitude
  print(v.mag())

  # Negative of the vector
  print(-v)

  # Scaling the vector
  print(v * 4)
  print(v / 2)

  # The following line if uncommented, produces an error
  # print(2 / v)

  # Addition of two vectors
  print(v + vector(1, 23, 2))

  # Subtraction of two vectors
  print(v - vector(7, 3, 11))

  # Dot product of two vectors
  print(v * vector(1, 23, 2))

  # Cross Product aka Vector Product of two vectors
  print(v ** vector(5, 2, 4))

Output

-2i + 3j - 7k
7.874007874011811
2i - 3j + 7k
-8i + 12j - 28k
-1.0i + 1.5j - 3.5k
-1i + 26j - 5k
-9i + 0j - 18k
53
26i - 27j - 19k

Conclusion

In this tutorial, we learnt about how to implement 3D vectors in Python with the help of Dunder methods. We use the basic principles of operator overloading to achieve this.

Leave a Reply