[Tutor] design of Point class
Gregory, Matthew
matt.gregory at oregonstate.edu
Mon Aug 23 20:36:31 CEST 2010
Steven D'Aprano wrote:
> It would surprise me greatly if numpy didn't already have such a class.
Yes, that is the first place I went looking, but I couldn't find such a class. I found one project using numpy for geometry objects (geometry-simple, http://code.google.com/p/geometry-simple/), but it doesn't look to be fully fleshed out.
> Other than using numpy, probably the simplest solution is to just
> subclass tuple and give it named properties and whatever other methods
> you want. Here's a simple version:
>
> class Point(tuple):
> def __new__(cls, *args):
> if len(args) == 1 and isinstance(args, tuple):
> args = args[0]
> for a in args:
> try:
> a+0
> except TypeError:
> raise TypeError('ordinate %s is not a number' % a)
> return super(Point, cls).__new__(cls, args)
> @property
> def x(self):
> return self[0]
> @property
> def y(self):
> return self[1]
> @property
> def z(self):
> return self[2]
> def dist(self, other):
> if isinstance(other, Point):
> if len(self) == len(other):
> sq_diffs = sum((a-b)**2 for (a,b) in zip(self, other))
> return math.sqrt(sq_diffs)
> else:
> raise ValueError('incompatible dimensions')
> raise TypeError('not a Point')
> def __repr__(self):
> return "%s(%r)" % (self.__class__.__name__, tuple(self))
>
>
> class Point2D(Point):
> def __init__(self, *args):
> if len(self) != 2:
> raise ValueError('need exactly two ordinates')
>
> class Point3D(Point):
> def __init__(self, *args):
> if len(self) != 3:
> raise ValueError('need exactly three ordinates')
>
> These classes gives you:
>
> * immutability;
> * the first three ordinates are named x, y and z;
> * any ordinate can be accessed by index with pt[3];
> * distance is only defined if the dimensions are the same;
> * nice string form;
> * input validation.
Thanks for this help. I'm curious as to why immutability would be an advantage here (or maybe that's not what you're suggesting). Typically, I would want to be able to do 'p.x = 10', so subclassing from a list (or numpy nd-array perhaps) would make more sense in my case?
Further, if I use setters, can I still use decorators as you've outlined or do I need to do use 'x = property(get, set)'. These are all new language constructs that I haven't encountered yet.
> What it doesn't give you (yet!) is:
>
> * distance between Points with different dimensions could easily be
> defined just by removing the len() comparison. zip() will
> automatically terminate at the shortest input, thus projecting the
> higher-dimension point down to the lower-dimension point;
> * other distance methods, such as Manhattan distance;
> * a nice exception when you as for (say) pt.z from a 2-D point, instead
> of raising IndexError;
> * point arithmetic (say, adding two points to get a third).
All good ideas, especially the different distance metrics to be defined in Point. I'm working on implementing these.
> An alternative would be to have the named ordinates return 0 rather than
> raise an error. Something like this would work:
>
> @property
> def y(self):
> try: return self[1]
> except IndexError: return 0
Is there an advantage to doing this? Wouldn't this make one falsely assume that y was defined and equal to 0?
thanks, matt
More information about the Tutor
mailing list