patternpythonMinor
2D Vector class
Viewed 0 times
classvectorstackoverflow
Problem
As part of a game engine I am creating, I am in the process of writing a bunch of math classes (vector2/3/4, matrix2/3/4, and quaternions to start with).
```
import math
import random
class Vec2(object):
def __init__(self, x, y):
self._x = float(x)
self._y = float(y)
@property
def x(self):
return self._x
@x.setter
def x(self, new_x):
self._x = float(new_x)
@property
def y(self):
return self._y
@y.setter
def y(self, new_y):
self._y = float(new_y)
def __add__(self, other):
types = (int, float)
if isinstance(self, types):
return Vec2(self + other.x, self + other.y)
elif isinstance(other, types):
return Vec2(self.x + other, self.y + other)
else:
return Vec2(self.x + other.x, self.y + other.y)
def __div__(self, other):
types = (int, float)
if isinstance(self, types):
self = Vec2(self, self)
elif isinstance(other, types):
other = Vec2(other, other)
x = self.x / other.x
y = self.y / other.y
return Vec2(x, y)
def __mul__(self, other):
types = (int, float)
if isinstance(self, types):
return Vec2(self other.x, self other.y)
elif isinstance(other, types):
return Vec2(self.x other, self.y other)
else:
return Vec2(self.x other.x, self.y other.y)
def __neg__(self):
return Vec2(-self.x, -self.y)
def __radd__(self, other):
return Vec2(self.x + other, self.y + other)
def __rdiv__(self, other):
return Vec2(other/self.x, other/self.y)
def __rmul__(self, other):
return Vec2(other self.x, other self.y)
def __rsub__(self, other):
return Vec2(other - self.x, other - self.y)
def __repr__(self):
return self.__str__()
def __str__(self):
return "Vec2: ({0}, {1})".fo
Vec2 class:```
import math
import random
class Vec2(object):
def __init__(self, x, y):
self._x = float(x)
self._y = float(y)
@property
def x(self):
return self._x
@x.setter
def x(self, new_x):
self._x = float(new_x)
@property
def y(self):
return self._y
@y.setter
def y(self, new_y):
self._y = float(new_y)
def __add__(self, other):
types = (int, float)
if isinstance(self, types):
return Vec2(self + other.x, self + other.y)
elif isinstance(other, types):
return Vec2(self.x + other, self.y + other)
else:
return Vec2(self.x + other.x, self.y + other.y)
def __div__(self, other):
types = (int, float)
if isinstance(self, types):
self = Vec2(self, self)
elif isinstance(other, types):
other = Vec2(other, other)
x = self.x / other.x
y = self.y / other.y
return Vec2(x, y)
def __mul__(self, other):
types = (int, float)
if isinstance(self, types):
return Vec2(self other.x, self other.y)
elif isinstance(other, types):
return Vec2(self.x other, self.y other)
else:
return Vec2(self.x other.x, self.y other.y)
def __neg__(self):
return Vec2(-self.x, -self.y)
def __radd__(self, other):
return Vec2(self.x + other, self.y + other)
def __rdiv__(self, other):
return Vec2(other/self.x, other/self.y)
def __rmul__(self, other):
return Vec2(other self.x, other self.y)
def __rsub__(self, other):
return Vec2(other - self.x, other - self.y)
def __repr__(self):
return self.__str__()
def __str__(self):
return "Vec2: ({0}, {1})".fo
Solution
In your first version, I don't understand your various
checks. Since you are building the
But as you figured out with the second version, you can remove a lot of repetitions with a base class or some kind of factory.
However, what seems a big mistake in the second version is the fact that you are downcasting any
Will raise
To avoid that, I don't have any better idea than to hack the
This allows you to call
This means that, in your various operators, you will need to return the right type, something along the lines of
The last thing that I wanted to mention is your usage of the properties. Why manage a separate variable when you have direct access to the underlying data structure? I would simplify
if isinstance(self, types):checks. Since you are building the
Vec2 class, you know that self is of type Vec2 or one of its subclasses, if any. So these checks will always evaluate to False: you’d better remove them.But as you figured out with the second version, you can remove a lot of repetitions with a base class or some kind of factory.
However, what seems a big mistake in the second version is the fact that you are downcasting any
VecX to a Vec after the first arithmetic operation. Meaning thatv1 = Vec2(4, 8)
v2 = Vec2(1, 1)
v3 = v1 + v2
print(v3.x, v3.y)Will raise
AttributeError for the v3.x and v3.y accesses. Because v3 is a Vec and not a Vec2.To avoid that, I don't have any better idea than to hack the
__init__ method a bit:def Vector(n):
class Vec(object):
def __init__(self, *args):
try:
data, = args
except ValueError:
data = np.array(args, dtype=np.float32)
assert len(data) == n
self.data = data
# other common methods
return Vec
class Vec2(Vector(2)):
# special methods for Vec2This allows you to call
Vec2(data) where data is a numpy array as well as Vec2(2, 0.8).This means that, in your various operators, you will need to return the right type, something along the lines of
return self.__class__(self.data + other.data) should do, for the addition.The last thing that I wanted to mention is your usage of the properties. Why manage a separate variable when you have direct access to the underlying data structure? I would simplify
Vec2 to:class Vec2(Vector(2)):
@property
def x(self):
return self.data[0]
@x.setter
def x(self, value):
self.data[0] = value # No need to manually convert to float, numpy will do it for us
@property
def y(self):
return self.data[1]
@y.setter
def y(self, value):
self.data[1] = valueCode Snippets
if isinstance(self, types):v1 = Vec2(4, 8)
v2 = Vec2(1, 1)
v3 = v1 + v2
print(v3.x, v3.y)def Vector(n):
class Vec(object):
def __init__(self, *args):
try:
data, = args
except ValueError:
data = np.array(args, dtype=np.float32)
assert len(data) == n
self.data = data
# other common methods
return Vec
class Vec2(Vector(2)):
# special methods for Vec2class Vec2(Vector(2)):
@property
def x(self):
return self.data[0]
@x.setter
def x(self, value):
self.data[0] = value # No need to manually convert to float, numpy will do it for us
@property
def y(self):
return self.data[1]
@y.setter
def y(self, value):
self.data[1] = valueContext
StackExchange Code Review Q#151205, answer score: 3
Revisions (0)
No revisions yet.