Pythonic way to handle coordinates

James Stroud jstroud at mbi.ucla.edu
Sat Jan 17 06:37:44 EST 2009


Pierre-Alain Dorange wrote:
> What is the elegant way to handle coordinates ?
> Do i need to continue using tuples or do i need to write a Point class.
> I feel a point class would be nice, because i could implement operators
> with it ? But i think Point class must exist allready ? 

My instinctive advice is to use a point class. My wise advice is to put 
it in a library you will maintain and reuse--or else you will find 
yourself re-writing point classes all of the time.

A Point class will also give you a place to consolidate all of those 
methods specific to manipulating points. Thanks to python magic methods, 
you can also design some nifty shortcuts to access the attributes of 
your point.

Here is a fun example for no other reason than I'm too tired to do 
anything important right now:


class Point(object):
   def __init__(self, axes):
     self.axes_names = axes.split()
     self.axes = dict(enumerate(self.axes_names))
     for axis in self.axes_names:
       setattr(self, axis, 0.0)
   def check_axis(self, i):
     if i not in self.axes:
       raise ValueError, "No such axis %s" % i
   def __setitem__(self, i, v):
     self.check_axis(i)
     setattr(self, self.axes[i], v)
   def __getitem__(self, i):
     self.check_axis(i)
     return getattr(self, self.axes[i])
   def __iter__(self):
     return (getattr(self, i) for i in self.axes_names)
   def move(self, other):
     for i, v in enumerate(other):
       self[i] += v
   def magnitude(self):
     import math
     return math.sqrt(sum(v**2 for v in self))


E.g.:


py> p = Point('x y z')
py> list(p)
     [0.0, 0.0, 0.0]
py> p.move([2, 3, 4])
py> p.x, p.y, p.z
     (2, 3, 4)
py> p[0], p[1], p[2]
     (2, 3, 4)
py> list(p)
     [2, 3, 4]
py> tuple(p)
     (2, 3, 4)
py> p.magnitude()
     5.3851648071345037
py> q = Point('x y z')
py> q.move([1, 2, 3])
py> p.move(q)
py> list(p)
     [3.0, 5.0, 7.0]
py> p[1] = 15.0
py> list(p)
     [3.0, 15.0, 7.0]


With the introduction of the second point, q, one can begin to see the 
necessity for a class factory that produces Point classes of the desired 
dimensionalities in desired coordinate systems:


def point(axes):
   class P(Point):
     def __init__(self, values):
       Point.__init__(self, axes)
       self.move(values)
   return P


Note that we have have not used metaclasses--which would require messier 
code but produce classes that would potentially behave a better as super 
classes.

The point function is used as simply as it is written:

py> Point3D = point('x y z')
py> p = Point3D([0.0, 0.0, 0.0])
py> list(p)
     [0.0, 0.0, 0.0]
py> q = Point3D([5.0, 6.0, 7.0])
py> list(q)
     [5.0, 6.0, 7.0]
py> p.move(q)
py> list(p)
     [5.0, 6.0, 7.0]


Incidentally, the classes returned by point are heritable, etc.


class Point3D(point('x y z')):
   def __repr__(self):
     return "Point3D(%s)" % list(self)
   def __add__(self, other):
     return Point3D(sum(t) for t in zip(self, other))


py> p = Point3D([8, 7, 6])
py> p
     Point3D([8.0, 7.0, 6.0])
py> str(p)
     'Point3D([8.0, 7.0, 6.0])'
py> q = Point3D([5, 4, 3])
py> r = p + q
py> r
     Point3D([13.0, 11.0, 9.0])


Now have fun with this stuff. I need to go to sleep.

James


-- 
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA  90095

http://www.jamesstroud.com



More information about the Python-list mailing list