1
\$\begingroup\$

I would like to share my code that is meant to help me add 2 vectors together. It is very simple and I would like to kindly ask you whether you could review my code. Also, I decided to keep separate variable for angle in degrees % 90. Do you think that was a good idea?

import math
def pythagorean_theorem(a,b, c=None):
 if (None is not a and None is not b) and None is c:
 return math.sqrt(float(a) ** 2 + float(b) ** 2)
 elif (a is None or b is None) and c is not None:
 if a is None:
 return math.sqrt(float(c)**2 - float(b)**2)
 else:
 return math.sqrt(float(c)**2 - float(a)**2)
 else:
 return -1
def find_x_component(vector1):
 alpha = math.radians(vector1[1] % 90)
 if vector1[1] > 0 and vector1[1] < 90:
 return (math.cos(alpha)) * vector1[0]
 elif vector1[1] > 90 and vector1[1] < 180:
 return 0 - ((math.sin(alpha)) * vector1[0])
 elif vector1[1] > 180 and vector1[1] < 270:
 return 0 - ((math.cos(alpha)) * vector1[0])
 elif vector1[1] > 270 and vector1[1] < 360:
 return (math.sin(alpha)) * vector1[0]
 elif vector1[1] == 180:
 return 0 - vector1[0]
 elif vector1[1] == 0:
 return vector1[0]
 else:
 return 0
def find_y_component(vector1):
 alpha = math.radians(vector1[1] % 90)
 if vector1[1] > 0 and vector1[1] < 90:
 return (math.sin(alpha)) * vector1[0]
 elif vector1[1] > 90 and vector1[1] < 180:
 return (math.cos(alpha)) * vector1[0]
 elif vector1[1] > 180 and vector1[1] < 270:
 return 0 - ((math.sin(alpha)) * vector1[0])
 elif vector1[1] > 270 and vector1[1] < 360:
 return 0 - (math.cos(alpha)) * vector1[0]
 elif vector1[1] == 270:
 return 0 - vector1[0]
 elif vector1[1] == 90:
 return vector1[0]
 else:
 return 0
def vector_addition(vector1, vector2):
 # Calculating X and Y components
 vector1X = find_x_component(vector1)
 vector1Y = find_y_component(vector1)
 vector2X = find_x_component(vector2)
 vector2Y = find_y_component(vector2)
 return [vector1X + vector2X, vector1Y + vector2Y, pythagorean_theorem(vector1X + vector2X,vector1Y + vector2Y)]
vectorA = [2, 45]
vectorB = [6, 73.5]
print(f'Coordinates of the endpoint of your vector are {vector_addition(vectorA, vectorB)[0:2]} and its magnitude is {vector_addition(vectorA, vectorB)[2]}.')
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Dec 1, 2018 at 22:09
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

This is actually a good place for a class. It allows you to define methods on the vector object itself and it also allows you to document your data format (it took me a bit to figure out that you initialize your vector as [magnitude, angle_from_x_axis_in_degrees]. It also means that you don't need to repeat yourself quite so often. Internally I would store the vector as its x and y components, since that is usually more useful (especially when adding two vectors):

from math import sqrt, cos, sin, radians
class Vector2D:
 """A 2D vector that supports addition, calculation of magnitude
 and initialization from (magnitude, angle)."""
 def __init__(self, x, y):
 """Standard constructor with x and y components."""
 self.x = x
 self.y = y
 @classmethod
 def from_mag_and_angle(cls, magnitude, angle):
 """Alternative constructor from magnitude and angle from x-axis in degrees."""
 x = magnitude * cos(radians(angle))
 y = magnitude * sin(radians(angle))
 return cls(x, y)
 def __add__(self, other):
 """v1 + v2 if both are 2D vectors"""
 if not isinstance(other, self.__class__):
 return NotImplemented
 return self.__class__(self.x + other.x, self.y + other.y)
 @property
 def magnitude(self):
 """Magnitude/length of vector"""
 return sqrt(self.x**2 + self.y**2)
 def __str__(self):
 """Readable representation of vector as list"""
 return f"[{self.x}, {self.y}]"

This uses two differently decorated methods. @classmethod is basically a way to define alternative constructors (here instead of giving the components we give the magnitude and angle). @property allows a method to be accessed like an attribute, so we can write v.magnitude, instead of v.magnitude(). This way we do not need to update the magnitude if we change the x or y components, it is calculated whenever we access the attribute.

Finally, getting the x and y components does not need all your special cases. The formula is valid in all four quadrants. Only when doing the opposite (getting the angle) do you need to special case (actually you don't because it is done for you in math.atan2).

The __add__ and __str__ methods are magic (or dunder) methods. They allow you to give custom classes built-in behaviour. The former allows you to write v1 + v2 and the latter defines str(v). __str__ is also called by str.format, so printing the coordinates of the vector is a lot easier.

You might want to define, e.g. __mul__ for multiplication with another vector or a scalar.

This can be used similarly to your code:

if __name__ == "__main__":
 vector_A = Vector2D.from_mag_and_angle(2, 45)
 vector_B = Vector2D.from_mag_and_angle(*[6, 73.5])
 vector_C = vector_A + vector_B
 print(f'Coordinates of the endpoint of your vector are {vector_C} and its magnitude is {vector_C.magnitude}.')

The execution of this code is protected by a if __name__ == "__main__": guard to allow importing from this script from another script.

You can use tuple unpacking to unpack your list into the two arguments of the classmethod (as done for the second vector) or just specify the parameters directly (as done for the first).

answered Dec 2, 2018 at 10:54
\$\endgroup\$
4
  • \$\begingroup\$ Thank you! I defined sub and mul for the scalar - vector case. I will finish the vector - vector case later however i have i question about how does the other thing which you take as an argument work. \$\endgroup\$ Commented Dec 2, 2018 at 16:05
  • \$\begingroup\$ @MatejNovosad What do you mean with "other thing which you take as an argument"? Which method and which argument? \$\endgroup\$ Commented Dec 2, 2018 at 16:15
  • \$\begingroup\$ def __add__(self, other): \$\endgroup\$ Commented Dec 2, 2018 at 16:23
  • \$\begingroup\$ @MatejNovosad When you write a + b, Python first tries to call a.__add__(b) behind the scene. If that returns NotImplemented, it tries to call b.__radd__(a). If that also fails it raises a TypeError, I think. So other is b in the first case and a in the second. \$\endgroup\$ Commented Dec 2, 2018 at 16:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.