A list with periodic boundary conditions

Y

youhvee

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

Thanks for any help.

-AM
 
R

Rhodri James

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
[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.
 
Y

youhvee

Rodhri,

thank you very much for your reply. I've tried to extend what you
suggested; I think it can be made to work, but you're right -- it gets
horrid rapidly. I'm at the start of what promises to be a large
project, and I want to make sure that I get my data structures right,
but my impression right now is that the memory efficiency of views is
negated by the computational complexity of their implementation. So I
think I will stick to copies and be done with it.

For the record, part of the reason I wanted a view was that my class
will be inherited from numpy.ndarray, where, by default, slices return
views.

Thanks again,

-AM

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
1

But I would also like to make slices. For instance I would like
[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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top