Re: [Tutor] design of Point class

2010-08-26 Thread Steven D'Aprano
On Thu, 26 Aug 2010 07:29:42 am Gregory, Matthew wrote:

 I hope you'll suffer me one more question on this thread.  In
 thinking about creating other distance methods (as you suggest), how
 best to create a generic enough interface, so that ANY distance
 metric could be used.  It seems like coding the Point class with
 multiple distance methods is not very flexible, especially if you
 wanted to iterate over those methods for any two points, e.g.

 class Point(tuple):
 def euclidean_distance(self, other):
 ...
 def manhattan_distance(self, other):
 ...
 def any_other_distance(self, other):
 ...

 Would this be a place for a generic get_distance with a
 DistanceMetric subclass as a parameter, e.g.

 class DistanceMetric(object):
 def distance(self, p1, p2):
 assert 0, 'Must be defined in subclasses'

 class EuclideanDistMetric(DistanceMetric):
 def distance(self, p1, p2):
 ...

 class Point(tuple):
 def get_distance(self, other, metric):
 distance = metric.distance(self, other)
 return distance


That could work, but it seems like a terribly heavyweight solution for a 
lightweight problem to me.

It doesn't save you any work -- you still have to code the distance 
method, but instead of creating a single method, you have to create an 
entire class. So it's more work.

I believe the simplest solution is to use the bound methods as first 
class functions:

pt = Point(23, 42)
# choose a distance 
if today == Tuesday:
dist = pt.manhattan_distance
else:
dist = pt.euclidean_distance
for other in list_of_other_points:
print(dist(other))



-- 
Steven D'Aprano
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-25 Thread Gregory, Matthew
Steven D'Aprano wrote:
 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):
 [snip]

 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).

I hope you'll suffer me one more question on this thread.  In thinking about 
creating other distance methods (as you suggest), how best to create a generic 
enough interface, so that ANY distance metric could be used.  It seems like 
coding the Point class with multiple distance methods is not very flexible, 
especially if you wanted to iterate over those methods for any two points, e.g.

class Point(tuple):
def euclidean_distance(self, other):
...
def manhattan_distance(self, other):
...
def any_other_distance(self, other):
...

Would this be a place for a generic get_distance with a DistanceMetric subclass 
as a parameter, e.g.

class DistanceMetric(object):
def distance(self, p1, p2):
assert 0, 'Must be defined in subclasses'

class EuclideanDistMetric(DistanceMetric):
def distance(self, p1, p2):
...

class Point(tuple):
def get_distance(self, other, metric):
distance = metric.distance(self, other)
return distance

I'm sure I don't have all my syntax correct.  Thanks for continued help.

matt
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-23 Thread Gregory, Matthew
Bob Gailer wrote:
 class PointND(list):
def __init__(self, *a_list):
  super(PointND, self).__init__(a_list)
 
def getSet(ix):
  def chklen(self):
if len(self)  ix + 1:
  raise AttributeError
  def get(self):
chklen(self)
return self[ix]
  def set(self, value):
chklen(self)
self[ix] = value
  return property(get, set)
 
def set(self, ix):
  return s
 
x = getSet(0)
y = getSet(1)
z = getSet(2)
 
[snip]

Bob and Hugo, thanks for enlightening me to class properties.  Obviously, I'm 
still on the learning curve.  Bob, I'm not seeing where the outer def 
set(self, ix) is used.  Am I missing something?

thanks, matt
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-23 Thread Gregory, Matthew
Hi Steven,

Steven D'Aprano wrote:
 Every time you change the interface of inherited methods, you probably
 shouldn't.
 
 Firstly, it probably breaks the Liskov Substitution Principle. The LSP
 says, essentially, if you subclass A to make B, you should be able to
 use a B anywhere you can use an A. (After all, instances of B are also
 instances of A.) Here's an example of what not to do:
 
 class Vehicle:
 def start(self, key):
 ignition.insert(key)
 ignition.turn()  # raises exception if out of fuel
 def go(self, key):
 self.start(key)
 self.handbrake = 'off'
 self.gear = 'drive'
 self.accelerate()
 
 class Truck(Vehicle):
 # add other truck-like methods
 
 class KeylessTruck(Truck):
 # Some military vehicles are designed to not require keys.
 # When on a battlefield, there's nothing worse than being
 # unable to find the car keys!
 def go(self):
 self.start()
 self.handbrake = 'off'
 self.gear = 'drive'
 self.accelerate()
 def start(self):
 ignition.turn()  # Actually a push button, but nevermind.
 
 Can you see the problem? If the caller is expecting a Truck, and pass a
 key to the truck.go() method, they will get an exception if you give
 them a KeylessTruck instead of a Truck. This is a Bad Thing.
 
 Secondly, changing method interfaces is not compatible with multiple
 inheritance and super(). You can probably get away with it if you stick
 to single inheritance, but it's still a bad thing to do.

Yes, this absolutely makes sense, but I'm less clear on how to solve it.  What 
I would think is that Vehicle shouldn't be defining start and this should be 
left up to a delegate within the subclasses?  Or am I showing my ignorance?

matt

matt
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-23 Thread bob gailer

 On 8/23/2010 1:09 PM, Gregory, Matthew wrote:

Bob Gailer wrote:

class PointND(list):
def __init__(self, *a_list):
  super(PointND, self).__init__(a_list)

def getSet(ix):
  def chklen(self):
if len(self)  ix + 1:
  raise AttributeError
  def get(self):
chklen(self)
return self[ix]
  def set(self, value):
chklen(self)
self[ix] = value
  return property(get, set)

def set(self, ix):
  return s

x = getSet(0)
y = getSet(1)
z = getSet(2)


[snip]

Bob and Hugo, thanks for enlightening me to class properties.  Obviously, I'm still on 
the learning curve.  Bob, I'm not seeing where the outer def set(self, ix) is 
used.  Am I missing something?


It should not be there! An accidental leftover.

But I notice other problems in my code which I am now attempting to fix. 
Back to you seen.


--
Bob Gailer
919-636-4239
Chapel Hill NC

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-23 Thread Gregory, Matthew
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
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-23 Thread bob gailer

 I am happier with this:

class PointND(list, object):

  def __init__(self, *a_list):
super(PointND, self).__init__(a_list)
self.maxIndex = len(self) - 1

  def getSet(ix, attName):
msg = '%s' object has no attribute '%s' % (p.__class__.__name__, 
attName)

def get(self):
  if self.maxIndex  ix:
raise AttributeError, msg
  return self[ix]
def set(self, value):
  if self.maxIndex  ix:
raise AttributeError, msg
  self[ix] = value
return property(get, set)

  x = getSet(0, 'x')
  y = getSet(1, 'y')
  z = getSet(2, 'z')

p = PointND(1,2,3)
assert (p.x, p.y, p.z) == (1, 2, 3)
p.x = 6; p.y = 9; p.z = 5
assert (p.x, p.y, p.z) == (6, 9, 5)

try:
  p = PointND(1,2)
  p.z = 3
except AttributeError:
  print 'Passed all tests'



--
Bob Gailer
919-636-4239
Chapel Hill NC

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-23 Thread Steven
On Tue, 24 Aug 2010 04:36:31 am Gregory, Matthew wrote:

 I'm curious as to why immutability would be an
 advantage here (or maybe that's not what you're suggesting). 

Immutability is *always* an advantage for numeric types.

You can use immutable objects as keys in dicts.

You can (if you want) optimise the class so that it caches instances, 
thus saving memory.

You can be sure that invariants remain true. If an immutable instance 
has to be, say, positive, once you've checked that it is positive once 
you can be sure than no function can change it to be negative.

Look at the standard Python numeric types:

int, long, float, decimal.Decimal, fractions.Fraction 

They are all immutable. That should tell you something :)


 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?

Why do you want to modify points rather than create new ones as needed?

In any case, you should not subclass from list. It makes no sense to 
have pt.sort() or pt.pop() or various other list-like methods. 
Subclassing list is not the way. If you insist on mutable points, then 
something like:

class MutablePoint(object):
def __init__(self, *ordinates):
self._ordinates = list(ordinates)
def __getitem__(self, i):
return self._ordinates[i]
def __setitem__(self, i, x):
self._ordinates[i] = x


and so forth.



 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.

In Python 2.5, the only way to pass a setter and/or deleter to property 
is by using the form x = property(getter, setter, deleter). In Python 
2.6, properties gain methods that let you do this:

@property
def x(self): return self._x
@x.setter
def x(self, value): self._x = value
@x.deleter
def x(self): del self._x

Note that you MUST use the same name (x in the above example) for the 
getter, setter and deleter.

[...]
  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?

That's why it's an alternative.

If you decide that for your application it makes sense to treat 
coordinates on the XY plane as equivalent to coordinates in the XYZ 
space with Z=0, that's the simplest way to implement it.



-- 
Steven
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-23 Thread Alan Gauld

Gregory, Matthew matt.greg...@oregonstate.edu wrote


class Vehicle:
def start(self, key):
def go(self, key):

class Truck(Vehicle):
# add other truck-like methods

class KeylessTruck(Truck):
# Some military vehicles are designed to not require keys.


Aside: Most modern high-end cars are keyless too! ;-)


def go(self):
def start(self):

Can you see the problem? If the caller is expecting a Truck, and 
pass a
key to the truck.go() method, they will get an exception if you 
give

them a KeylessTruck instead of a Truck. This is a Bad Thing.


Yes, this absolutely makes sense, but I'm less clear on how to solve 
it.


There are several ways. Where you cannot change the original
interface - which wrongly assumed that all vehicles need keys - you
can introduce a default key parameter in which the method just ignores
the key and provides a default value(None?) if not provided.

Its messy and I'd definitely comment it to make it clear the key is
only to preserve the inherited interface but its probably the simplest 
option.



What I would think is that Vehicle shouldn't be defining start


Defining start is probably OK (I can't think of a vehicle that doesn't
start in some way or other), but requiring a key is a mistake since
many vehicles don't use keys (think rickshaw or trolley bus etc)


and this should be left up to a delegate within the subclasses?


I'm not sure a delegate would help here, especially if you want
to start a list of vehicles.


Or am I showing my ignorance?


No, just uncovering the challenges of OO design. There is no
perfect solution, all models are approximations. Experience tends
to build better approximations but never perfect ones.

And sometimes breaking the LSP and working round the consequences
(try/except...) is the best - ie most pragmatic - solution. But that
should be the least favourite choice, try to maintain interfaces if 
possible.


HTH,

--
Alan Gauld
Author of the Learn to Program web site
http://www.alan-g.me.uk/


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-21 Thread bob gailer

 On 8/20/2010 8:35 PM, bob gailer wrote:

After yet more thought it gets even more better: I even added a unit test.

class PointND(list):
  def __init__(self, *a_list):
super(PointND, self).__init__(a_list)

  def getSet(ix):
def chklen(self):
  if len(self)  ix + 1:
raise AttributeError
def get(self):
  chklen(self)
  return self[ix]
def set(self, value):
  chklen(self)
  self[ix] = value
return property(get, set)

  def set(self, ix):
return s

  x = getSet(0)
  y = getSet(1)
  z = getSet(2)

p = PointND(1,2,3)
assert (p.x, p.y, p.z) == (1, 2, 3)
p.x = 6; p.y = 9; p.z = 5
assert (p.x, p.y, p.z) == (6, 9, 5)

try:
  p = PointND(1,2)
  p.z = 3
except AttributeError:
  print 'Passed all tests'
except:
  print 'some other exception'



--
Bob Gailer
919-636-4239
Chapel Hill NC

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Alex Hall
On 8/20/10, Gregory, Matthew matt.greg...@oregonstate.edu wrote:
 Hi all,

 I often struggle with object design and inheritance.  I'd like opinions on
 how best to design a Point class to be used in multiple circumstances.

 I typically deal with geographic (either 2D or 3D) data, yet there are
 occasions when I need n-dimensional points as well.  My thought was to
 create a superclass which was an n-dimensional point and then subclass that
 to 2- and 3-dimensional cases.  The rub to this is that in the n-dimensional
 case, it probably makes most sense to store the actual coordinates as a list
 whereas with the 2- and 3-D cases, I would want 'named' variables, such as
 x, y, z.

 Here's a (very rough) first cut at the constructor and a generic distance
 function for the n-dimensional case:

 class PointND(object):
 def __init__(self, a_list):
 self.a_list = a_list[:]

 def distance(self, right):
 assert(len(self.coord) == len(right))
 squared_diffs = [(i-j)*(i-j) for (i,j) in zip(self.coord, right)]
 return math.sqrt(sum(squared_diffs))

 But how can I subclass this in such a way to be able to:

 1) Have named variables in the 2- and 3-D cases
I assume you will have separate subclasses for both 2d and 3d, so just
set up each subclass's __init__ to accept the proper amount and order
of arguments.
 2) Be able to initialize with separate passed values, e.g. 'p = Point2D(3.0,
 5.0)'
rather than passing in a list
class 2dPoint(point):
  def __init__(self, x, y):
self.x=x
self.y=y
self.points=[x,y]


 Or am I totally off on thinking this is a good place for inheritance?
I would use subclassing. Designed right, this gives you access to
common functions like distance but also lets you customize each
subclass with specific methods unique to that subclass. Just my 2
cents, though, and I am not too experienced here.

 Thanks for any help,
 matt
 ___
 Tutor maillist  -  Tutor@python.org
 To unsubscribe or change subscription options:
 http://mail.python.org/mailman/listinfo/tutor



-- 
Have a great day,
Alex (msg sent from GMail website)
mehg...@gmail.com; http://www.facebook.com/mehgcap
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread bob gailer

 On 8/20/2010 11:45 AM, Gregory, Matthew wrote:

Hi all,

I often struggle with object design and inheritance.  I'd like opinions on how 
best to design a Point class to be used in multiple circumstances.

I typically deal with geographic (either 2D or 3D) data, yet there are 
occasions when I need n-dimensional points as well.  My thought was to create a 
superclass which was an n-dimensional point and then subclass that to 2- and 
3-dimensional cases.  The rub to this is that in the n-dimensional case, it 
probably makes most sense to store the actual coordinates as a list whereas 
with the 2- and 3-D cases, I would want 'named' variables, such as x, y, z.

Here's a (very rough) first cut at the constructor and a generic distance 
function for the n-dimensional case:

class PointND(object):
 def __init__(self, a_list):
 self.a_list = a_list[:]

 def distance(self, right):
 assert(len(self.coord) == len(right))
 squared_diffs = [(i-j)*(i-j) for (i,j) in zip(self.coord, right)]
 return math.sqrt(sum(squared_diffs))

But how can I subclass this in such a way to be able to:

1) Have named variables in the 2- and 3-D cases
2) Be able to initialize with separate passed values, e.g. 'p = Point2D(3.0, 
5.0)' rather than passing in a list


One class fits all:

class PointND(list):
def __init__(self, *a_list):
super(PointND, self).__init__(a_list)
if len(self)= 2:
self.x = self[0]
if len(self) == 2:
self.y = self[1]


--
Bob Gailer
919-636-4239
Chapel Hill NC

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Wayne Werner
On Fri, Aug 20, 2010 at 10:45 AM, Gregory, Matthew 
matt.greg...@oregonstate.edu wrote:

 Hi all,

 I often struggle with object design and inheritance.  I'd like opinions on
 how best to design a Point class to be used in multiple circumstances.

 I typically deal with geographic (either 2D or 3D) data, yet there are
 occasions when I need n-dimensional points as well.  My thought was to
 create a superclass which was an n-dimensional point and then subclass that
 to 2- and 3-dimensional cases.  The rub to this is that in the n-dimensional
 case, it probably makes most sense to store the actual coordinates as a list
 whereas with the 2- and 3-D cases, I would want 'named' variables, such as
 x, y, z.

 Here's a (very rough) first cut at the constructor and a generic distance
 function for the n-dimensional case:

 class PointND(object):
def __init__(self, a_list):
self.a_list = a_list[:]

def distance(self, right):
assert(len(self.coord) == len(right))
squared_diffs = [(i-j)*(i-j) for (i,j) in zip(self.coord, right)]
return math.sqrt(sum(squared_diffs))

 But how can I subclass this in such a way to be able to:

 1) Have named variables in the 2- and 3-D cases
 2) Be able to initialize with separate passed values, e.g. 'p =
 Point2D(3.0, 5.0)'
   rather than passing in a list


class Point2D(PointND):
def __init__(self, x = 0, y = 0):
super(Point2D, self).__init__([x,y])
self.x = 0
self.y = 0

though you wouldn't be able to directly modify the values, or you'll lose
the distance function. You'd have to create setter functions, and as such
should rename x and y to _x and _y, to indicate that sure you *can* touch
these, but really you shouldn't.

For the 3d, you'd just add a z param, although to really generalize your ND
class you could do this instead:

class PointND(object):
   def __init__(self, x=0, y=0, z=0, a_list=None):
   if a_list is not None:
   self.a_list = a_list[:]
   self.x = x
   self.y = y
   self.z = z

   def coords(self):
   return [self.x, self.y, self.z] + self.a_list

   ...

Then your subclass takes less effort:

class Point2D(PointND):
def __init__(self, x=0, y=0):
super(Point2D, self).__init__(x,y)

and this allows you to access point.x, point.y, and point.z directly.

Of course you could also subclass list with ND and just use descriptors for
self[0], self[1], and self[2]:
http://users.rcn.com/python/download/Descriptor.htm

HTH,
Wayne
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Gregory, Matthew
Wayne Werner wrote:
 class Point2D(PointND):
     def __init__(self, x = 0, y = 0):
         super(Point2D, self).__init__([x,y])
         self.x = 0
         self.y = 0
 
 though you wouldn't be able to directly modify the values, or you'll 
 lose the distance function. You'd have to create setter functions, and as 
 such should rename x and y to _x and _y, to indicate that sure you *can* 
 touch 
 these, but really you shouldn't.
 
 For the 3d, you'd just add a z param, although to really generalize your 
 ND class you could do this instead:
 
 class PointND(object):
    def __init__(self, x=0, y=0, z=0, a_list=None):
        if a_list is not None:
            self.a_list = a_list[:]
        self.x = x
        self.y = y
        self.z = z
   
    def coords(self):
        return [self.x, self.y, self.z] + self.a_list
    ...
 
 Then your subclass takes less effort:
 
 class Point2D(PointND):
     def __init__(self, x=0, y=0):
         super(Point2D, self).__init__(x,y)
 
 and this allows you to access point.x, point.y, and point.z directly.
 
 Of course you could also subclass list with ND and just use descriptors 
 for self[0], self[1], and self[2]:
 http://users.rcn.com/python/download/Descriptor.htm

Thanks all for good suggestions.  I'm intrigued by the idea of subclassing list 
(suggested by both Bob and Wayne) and using x, y and z as descriptors to the 
elements in the list.  Obviously, it's important that the descriptors (x,y,z) 
stay in sync with the list itself so that:

   p = PointND(1,2,3)
   p.x = 10
   p
  [10,2,3]

From what I understood of the link Wayne sent, I should be able to use __set__ 
to create this relationship between the labels and the list, but I'm totally 
lost on how to do this.  It seems like x,y,z need to be instances of 
descriptor objects who have values that are associated with the list.

In the mean time, I've overridden __setattr__ to enforce this, but it looks a 
bit crufty.  Any further suggestions are most welcome.

class PointND(list):
def __init__(self, *a_list):
super(PointND, self).__init__(a_list)
if len(self) = 3:
self.x = self[0]
if len(self) = 2 and len(self) = 3:
self.y = self[1]
if len(self) == 3:
self.z = self[2]

def __setattr__(self, attr, value):
if attr in ('x', 'y', 'z'):
self.__dict__[attr] = value
if attr == 'x':
self[0] = value
elif attr == 'y':
self[1] = value
else:
self[2] = value 
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Hugo Arts
On Fri, Aug 20, 2010 at 12:55 PM, Gregory, Matthew
matt.greg...@oregonstate.edu wrote:
 Wayne Werner wrote:
 class Point2D(PointND):
     def __init__(self, x = 0, y = 0):
         super(Point2D, self).__init__([x,y])
         self.x = 0
         self.y = 0

 though you wouldn't be able to directly modify the values, or you'll
 lose the distance function. You'd have to create setter functions, and as
 such should rename x and y to _x and _y, to indicate that sure you *can* 
 touch
 these, but really you shouldn't.

 For the 3d, you'd just add a z param, although to really generalize your
 ND class you could do this instead:

 class PointND(object):
    def __init__(self, x=0, y=0, z=0, a_list=None):
        if a_list is not None:
            self.a_list = a_list[:]
        self.x = x
        self.y = y
        self.z = z

    def coords(self):
        return [self.x, self.y, self.z] + self.a_list
    ...

 Then your subclass takes less effort:

 class Point2D(PointND):
     def __init__(self, x=0, y=0):
         super(Point2D, self).__init__(x,y)

 and this allows you to access point.x, point.y, and point.z directly.

 Of course you could also subclass list with ND and just use descriptors
 for self[0], self[1], and self[2]:
 http://users.rcn.com/python/download/Descriptor.htm

 Thanks all for good suggestions.  I'm intrigued by the idea of subclassing 
 list (suggested by both Bob and Wayne) and using x, y and z as descriptors to 
 the elements in the list.  Obviously, it's important that the descriptors 
 (x,y,z) stay in sync with the list itself so that:

   p = PointND(1,2,3)
   p.x = 10
   p
  [10,2,3]

 From what I understood of the link Wayne sent, I should be able to use 
 __set__ to create this relationship between the labels and the list, but I'm 
 totally lost on how to do this.  It seems like x,y,z need to be instances of 
 descriptor objects who have values that are associated with the list.

 In the mean time, I've overridden __setattr__ to enforce this, but it looks a 
 bit crufty.  Any further suggestions are most welcome.

 class PointND(list):
    def __init__(self, *a_list):
        super(PointND, self).__init__(a_list)
        if len(self) = 3:
            self.x = self[0]
        if len(self) = 2 and len(self) = 3:
            self.y = self[1]
        if len(self) == 3:
            self.z = self[2]

    def __setattr__(self, attr, value):
        if attr in ('x', 'y', 'z'):
            self.__dict__[attr] = value
            if attr == 'x':
                self[0] = value
            elif attr == 'y':
                self[1] = value
            else:
                self[2] = value

perhaps properties could be of some use?

from operator import itemgetter, setitem

def named_index(index):
getter = itemgetter(index)
setter = lambda self, val: setitem(self, index, val)
return property(getter, setter)

class NPoint(list):
x = named_index(0)
y = named_index(1)
z = named_index(2)

p = NPoint([3, 4, 50])
print p.x, p.y, p.z
p.x = p.y + 13
print p

Note that trying to access z when there are not enough items in the
list will raise an IndexError, not an AttributeError. You might want
to adjust the getter/setter functions a little.

Alternatively, I'm not sure if you can add properties in __init__ or
__new__, but if not that, you can probably write a metaclass that adds
in the right properties based on list length.

Hugo
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread bob gailer

 On 8/20/2010 1:55 PM, Gregory, Matthew wrote:

Wayne Werner wrote:

class Point2D(PointND):
 def __init__(self, x = 0, y = 0):
 super(Point2D, self).__init__([x,y])
 self.x = 0
 self.y = 0

though you wouldn't be able to directly modify the values, or you'll
lose the distance function. You'd have to create setter functions, and as
such should rename x and y to _x and _y, to indicate that sure you *can* touch
these, but really you shouldn't.

For the 3d, you'd just add a z param, although to really generalize your
ND class you could do this instead:

class PointND(object):
def __init__(self, x=0, y=0, z=0, a_list=None):
if a_list is not None:
self.a_list = a_list[:]
self.x = x
self.y = y
self.z = z
   
def coords(self):

return [self.x, self.y, self.z] + self.a_list
...

Then your subclass takes less effort:

class Point2D(PointND):
 def __init__(self, x=0, y=0):
 super(Point2D, self).__init__(x,y)

and this allows you to access point.x, point.y, and point.z directly.

Of course you could also subclass list with ND and just use descriptors
for self[0], self[1], and self[2]:
http://users.rcn.com/python/download/Descriptor.htm

Thanks all for good suggestions.  I'm intrigued by the idea of subclassing list 
(suggested by both Bob and Wayne) and using x, y and z as descriptors to the 
elements in the list.  Obviously, it's important that the descriptors (x,y,z) 
stay in sync with the list itself so that:

 p = PointND(1,2,3)
 p.x = 10
 p
   [10,2,3]

 From what I understood of the link Wayne sent, I should be able to use 
__set__ to create this relationship between the labels and the list, but I'm 
totally lost on how to do this.  It seems like x,y,z need to be instances of 
descriptor objects who have values that are associated with the list.

In the mean time, I've overridden __setattr__ to enforce this, but it looks a 
bit crufty.  Any further suggestions are most welcome.

class PointND(list):
 def __init__(self, *a_list):
 super(PointND, self).__init__(a_list)
 if len(self)= 3:
 self.x = self[0]
 if len(self)= 2 and len(self)= 3:
 self.y = self[1]
 if len(self) == 3:
 self.z = self[2]

 def __setattr__(self, attr, value):
 if attr in ('x', 'y', 'z'):
 self.__dict__[attr] = value
 if attr == 'x':
 self[0] = value
 elif attr == 'y':
 self[1] = value
 else:
 self[2] = value


class PointND(list):
def __init__(self, *a_list):
super(PointND, self).__init__(a_list)

def get(self, ix):
if len(self)=  ix + 1:
return self[ix]
else:
raise AttributeError

def set(self, ix, value):
if len(self)=  ix + 1:
self[ix] = value
else:
raise AttributeError

def getx(self):
return self.get(0)
def setx(self, value):
self.set(0, value)
x  =  property(getx,  setx)

def gety(self):
return self.get(1)
def sety(self, value):
self.set(1, value)
y  =  property(gety,  sety)

# repeat for z



--
Bob Gailer
919-636-4239
Chapel Hill NC

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread bob gailer

 On 8/20/2010 1:55 PM, Gregory, Matthew wrote:

Wayne Werner wrote:

class Point2D(PointND):
 def __init__(self, x = 0, y = 0):
 super(Point2D, self).__init__([x,y])
 self.x = 0
 self.y = 0

though you wouldn't be able to directly modify the values, or you'll
lose the distance function. You'd have to create setter functions, and as
such should rename x and y to _x and _y, to indicate that sure you *can* touch
these, but really you shouldn't.

For the 3d, you'd just add a z param, although to really generalize your
ND class you could do this instead:

class PointND(object):
def __init__(self, x=0, y=0, z=0, a_list=None):
if a_list is not None:
self.a_list = a_list[:]
self.x = x
self.y = y
self.z = z

def coords(self):
return [self.x, self.y, self.z] + self.a_list
...

Then your subclass takes less effort:

class Point2D(PointND):
 def __init__(self, x=0, y=0):
 super(Point2D, self).__init__(x,y)

and this allows you to access point.x, point.y, and point.z directly.

Of course you could also subclass list with ND and just use descriptors
for self[0], self[1], and self[2]:
http://users.rcn.com/python/download/Descriptor.htm

Thanks all for good suggestions.  I'm intrigued by the idea of subclassing list 
(suggested by both Bob and Wayne) and using x, y and z as descriptors to the 
elements in the list.  Obviously, it's important that the descriptors (x,y,z) 
stay in sync with the list itself so that:

 p = PointND(1,2,3)
 p.x = 10
 p
   [10,2,3]

 From what I understood of the link Wayne sent, I should be able to use 
__set__ to create this relationship between the labels and the list, but I'm 
totally lost on how to do this.  It seems like x,y,z need to be instances of 
descriptor objects who have values that are associated with the list.

In the mean time, I've overridden __setattr__ to enforce this, but it looks a 
bit crufty.  Any further suggestions are most welcome.

class PointND(list):
 def __init__(self, *a_list):
 super(PointND, self).__init__(a_list)
 if len(self)= 3:
 self.x = self[0]
 if len(self)= 2 and len(self)= 3:
 self.y = self[1]
 if len(self) == 3:
 self.z = self[2]

 def __setattr__(self, attr, value):
 if attr in ('x', 'y', 'z'):
 self.__dict__[attr] = value
 if attr == 'x':
 self[0] = value
 elif attr == 'y':
 self[1] = value
 else:
 self[2] = value


After more thought it gets even better:

class PointND(list):
  def __init__(self, *a_list):
super(PointND, self).__init__(a_list)

  def get(ix):
def g(self):
  if len(self) =  ix + 1:
return self[ix]
  else:
raise AttributeError
return g

  def set(ix):
def s(self, value):
  if len(self) =  ix + 1:
self[ix] = value
  else:
raise AttributeError
return s

  x = property(get(0), set(0))
  y = property(get(1), set(1))
  z = property(get(2), set(2))



--
Bob Gailer
919-636-4239
Chapel Hill NC

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Alan Gauld


Gregory, Matthew matt.greg...@oregonstate.edu wrote


I typically deal with geographic (either 2D or 3D) data, yet
there are occasions when I need n-dimensional points as well.


Thats OK.


The rub to this is that in the n-dimensional case, it probably
makes most sense to store the actual coordinates as a list
whereas with the 2- and 3-D cases, I would want 'named'
variables, such as x, y, z.


Thats OK too but adds some overhead.

Remember that inheritance should be based on behaviour not data
so determine the common methods of a Point. Write the ND version
of those methods.

Then create subclasses for 2D and 3D which convert the named args
to a list equivalent then call the superclass ND versions.

Every time you change the interface of inherited methods you
create for yourself extra work in converting types to match the
superclass. But that is often easier than rewriting the methods
from scratch.

Just remember that to get the best out of polymorphism - the primary
reason for inheritance - you should be able to mix n match instances 
of
the subclasses with instances of the superclass - the Liskov 
Substitution

Principle(LSP) - and if you break that you lose for yourself much of
the power of inheritance.

HTH,

--
Alan Gauld
Author of the Learn to Program web site
http://www.alan-g.me.uk/



Here's a (very rough) first cut at the constructor and a generic 
distance function for the n-dimensional case:


class PointND(object):
   def __init__(self, a_list):
   self.a_list = a_list[:]

   def distance(self, right):
   assert(len(self.coord) == len(right))
   squared_diffs = [(i-j)*(i-j) for (i,j) in zip(self.coord, 
right)]

   return math.sqrt(sum(squared_diffs))

But how can I subclass this in such a way to be able to:

1) Have named variables in the 2- and 3-D cases
2) Be able to initialize with separate passed values, e.g. 'p = 
Point2D(3.0, 5.0)'

  rather than passing in a list

Or am I totally off on thinking this is a good place for 
inheritance?


Thanks for any help,
matt
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor




___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Steven D'Aprano
On Sat, 21 Aug 2010 01:45:18 am Gregory, Matthew wrote:
 Hi all,

 I often struggle with object design and inheritance.  I'd like
 opinions on how best to design a Point class to be used in multiple
 circumstances.

 I typically deal with geographic (either 2D or 3D) data, yet there
 are occasions when I need n-dimensional points as well.  My thought
 was to create a superclass which was an n-dimensional point and then
 subclass that to 2- and 3-dimensional cases.  The rub to this is that
 in the n-dimensional case, it probably makes most sense to store the
 actual coordinates as a list whereas with the 2- and 3-D cases, I
 would want 'named' variables, such as x, y, z.

It would surprise me greatly if numpy didn't already have such a class.


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.


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).

But you can't expect me to do all your work :)


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




-- 
Steven D'Aprano
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Steven D'Aprano
On Sat, 21 Aug 2010 08:08:56 am Alan Gauld wrote:
 Every time you change the interface of inherited methods you
 create for yourself extra work in converting types to match the
 superclass. But that is often easier than rewriting the methods
 from scratch.

Every time you change the interface of inherited methods, you probably 
shouldn't.

Firstly, it probably breaks the Liskov Substitution Principle. The LSP 
says, essentially, if you subclass A to make B, you should be able to 
use a B anywhere you can use an A. (After all, instances of B are also 
instances of A.) Here's an example of what not to do:

class Vehicle:
def start(self, key):
ignition.insert(key)
ignition.turn()  # raises exception if out of fuel
def go(self, key):
self.start(key)
self.handbrake = 'off'
self.gear = 'drive'
self.accelerate()

class Truck(Vehicle):
# add other truck-like methods

class KeylessTruck(Truck):
# Some military vehicles are designed to not require keys.
# When on a battlefield, there's nothing worse than being 
# unable to find the car keys!
def go(self):
self.start()
self.handbrake = 'off'
self.gear = 'drive'
self.accelerate()
def start(self):
ignition.turn()  # Actually a push button, but nevermind.


Can you see the problem? If the caller is expecting a Truck, and pass a 
key to the truck.go() method, they will get an exception if you give 
them a KeylessTruck instead of a Truck. This is a Bad Thing.


Secondly, changing method interfaces is not compatible with multiple 
inheritance and super(). You can probably get away with it if you stick 
to single inheritance, but it's still a bad thing to do.




-- 
Steven D'Aprano
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] design of Point class

2010-08-20 Thread Steven D'Aprano
On Sat, 21 Aug 2010 11:50:40 am Steven D'Aprano wrote:
 On Sat, 21 Aug 2010 08:08:56 am Alan Gauld wrote:
  Every time you change the interface of inherited methods you
  create for yourself extra work in converting types to match the
  superclass. But that is often easier than rewriting the methods
  from scratch.

 Every time you change the interface of inherited methods, you
 probably shouldn't.

Oops, that's a generic you, not Alan. After all, he went on to mention 
Liskov in the very next paragraph.



-- 
Steven D'Aprano
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor