[Tutor] design of Point class

Hugo Arts hugo.yoshi at gmail.com
Fri Aug 20 23:32:12 CEST 2010

```On Fri, Aug 20, 2010 at 12:55 PM, Gregory, Matthew
<matt.gregory at 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]:
>
> 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
```