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]}.')
1 Answer 1
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).
-
\$\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\$Matej Novosad– Matej Novosad2018年12月02日 16:05:03 +00:00Commented 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\$Graipher– Graipher2018年12月02日 16:15:54 +00:00Commented Dec 2, 2018 at 16:15
-
\$\begingroup\$ def __add__(self, other): \$\endgroup\$Matej Novosad– Matej Novosad2018年12月02日 16:23:30 +00:00Commented Dec 2, 2018 at 16:23
-
\$\begingroup\$ @MatejNovosad When you write
a + b
, Python first tries to calla.__add__(b)
behind the scene. If that returnsNotImplemented
, it tries to callb.__radd__(a)
. If that also fails it raises aTypeError
, I think. Soother
isb
in the first case anda
in the second. \$\endgroup\$Graipher– Graipher2018年12月02日 16:26:51 +00:00Commented Dec 2, 2018 at 16:26
Explore related questions
See similar questions with these tags.