I have had little experience with OO programming, being a typical engineer I tend to hack together functional code. However, I'm trying to change that and I see value in implementing OO when modelling the physical world. I've written my first ever class for a position vector. I want an instance of the class to be able to store both the cartesian [x, y, z] and polar [z, theta, phi] coordinates of a position. Updating one attribute automatically updates the others. This class will be later be reused in other classes via composition.
I want an instance of my class to do the following:
>> p = PositionVector([15,5,5]) >> p.cartesian [15,5,5] >> p.polar [16.583123951777, 0.0, 1.2645189576252271] >> p.x = 3 >> p.cartesian [3,5,5] >> p.polar [7.681145747868608, 0.7853981633974483, 0.8619682853367363] >> p.theta = 0.5 >> p.cartesian [5.117141702137862, 2.7955072527614058, 5.0] >> p.polar [7.681145747868608, 0.5, 0.8619682853367363] The class I've written so far is below and while it works as expected, my limited understanding of Python classes and the property function tells me there's a better way to do this. I don't know if the methods for x y z r theta and phi need to be fully written since they are simply elements within each list/array but can't figure out how to get around this. I also don't know if this is the safest and most pythonic way of structuring a class object:
class PositionVector(object): def __init__(self, vector, system='cartesian'): if system == 'cartesian': self.cartesian = vector self.polar = self.position_vector_polar(self.cartesian) if system == 'polar': self.polar = vector self.cartesian = self.position_vector_cartesian(self.polar) @property def cartesian(self): return self._cartesian @cartesian.setter def cartesian(self, value): self._cartesian = value self._polar = self.position_vector_polar(self._cartesian) self._x, self._y, self._z = self._cartesian self._r, self._theta, self._phi = self._polar @property def polar(self): return self._polar @polar.setter def polar(self, value): self._polar = value self._cartesian = self.position_vector_cartesian(self._polar) self._x, self._y, self._z = self.cartesian self._r, self._theta, self._phi = self.polar @property def x(self): return self._x @x.setter def x(self, value): self._x = value self._cartesian[0] = self._x self._polar = self.position_vector_polar(self._cartesian) # repeat above for y and z variables @property def r(self): return self._r @r.setter def r(self, value): self._r = value self._polar[0] = self._r self._cartesian = self.position_vector_cartesian(self._polar) # repeat above for thets and phi variables def position_vector_cartesian(self, polar): r, theta, phi = polar x = r * math.cos(theta) * math.sin(phi) y = r * math.sin(theta) * math.sin(phi) z = r * math.cos(phi) return [x, y, z] def position_vector_polar(self, cartesian): x, y, z = cartesian r = math.sqrt(x ** 2 + y ** 2 + z ** 2) theta = math.atan(y/x) phi = math.acos(z/r) return [r, theta, phi] Note that the lists will be be replaced with NumPy arrays so I can perform algebra on the vectors, but for now I just want to get the class structure correct.