A list with periodic boundary conditions

Rhodri James rhodri at wildebst.demon.co.uk
Thu May 21 19:20:01 EDT 2009


On Thu, 21 May 2009 13:08:39 +0100, <youhvee at googlemail.com> wrote:

> Hi,
>
> I'm trying to create a new class of list that has periodic boundary
> conditions.
>
> Here's what I have so far:
>
> class wrappedList(list):
>     def __getitem__(self, p):
>         return list.__getitem__(self, p%len(self))
>     def __setitem__(self, p, v):
>         list.__setitem__(self, p%len(self), v)
>
>>>>> a=wrappedList(range(10))
>>>>> a[1]
> 1
>>>>> a[11]
> 1
>
> But I would also like to make slices. For instance I would like
>>>>> a[8:11]
> [8, 9, 0]
>
> I can do it by copying, but I want to return a view to the original
> data, and I have no idea where to start. It seems that the slice needs
> to retain some knowledge of the list from which it derived. i.e. it
> needs to know that it is a slice. Any ideas on how I can extend this
> to allow views?

Reading the docs, this looks like a very messy area of Python 2.x in
that for a builtin like `list` you have to provide a __getslice__ method
despite it being deprecated and only dealing with simple slices.  Good
luck with that.  In Python 3, you just have to deal with the fact that
your __getitem__ and __setitem__ `p` arguments can be slice objects
instead of integers.

If you're set on making your slices views on the original (which means
that changes to the slice will change the original, unlike normal lists!)
then you need to extend your class to remember what it's viewing, and
what the start, stop and step of the slice were.  When it's a view, it
passes (massaged) requests up to its parent.  There's a cute (but likely
inefficient) way of doing this that uses the fact that your view is still
a list under the hood, and stashes the parental indices in it.  This will
also make len() work correctly, for a bonus :-)  It gets quite horrid
quite fast, but here's a very incomplete untested skeleton:

class wrappedList(list):
   def __init__(self, parent=None, *args):
     list.__init__(self, *args)
     self.parent = parent

   def __getitem__(self, p):
     if self.parent is None:
       self.primary_getitem(p)
     else:
       self.view_getitem(p)

   def view_getitem(self, p):
     return self.parent[list.__getitem__(self, p%len(self))]

...and similarly for __setitem__, where primary_getitem() is your
previous __getitem__ method.  All four need to be modified to cope
with slices, of course, and to create new wrappedList objects.
Something like this:

   if isinstance(p, slice):
     if p.start is None:
       start = 0
     else:
       start = p.start
     if p.step is None:
       step = 1
     else:
       step = p.step
     indices = range(start, p.stop, step)
     return wrappedList(indices, parent=self)

This will go horribly, horribly wrong if you delete anything from
your original list, but I can't off-hand think of a view method
that won't.

-- 
Rhodri James *-* Wildebeeste Herder to the Masses



More information about the Python-list mailing list