Can I create a "view" on a Python list?
I have a large list l
. I want to create a view from element 4 to 6. I can do it with sequence slice.
>>> l = range(10)
>>> lv = l[3:6]
>>> lv
[3, 4, 5]
However lv
is a copy of a slice of l
. If I change the underlying list, lv
does not reflect the change.
>>> l[4] = -1
>>> lv
[3, 4, 5]
Vice versa I want modification on lv
reflect in l
as well. Other than that the list size are not going to be changed.
I'm not looking forward to build a big class to do this. I'm just hoping other Python gurus may know some hidden language trick. Ideally I hope it can be like pointer arithmetic in C:
int lv[] = l + 3;
There is no "list slice" class in the Python standard library (nor is one built-in). So, you do need a class, though it need not be big -- especially if you're content with a "readonly" and "compact" slice. E.g.:
import collections
class ROListSlice(collections.Sequence):
def __init__(self, alist, start, alen):
self.alist = alist
self.start = start
self.alen = alen
def __len__(self):
return self.alen
def adj(self, i):
if i<0: i += self.alen
return i + self.start
def __getitem__(self, i):
return self.alist[self.adj(i)]
This has some limitations (doesn't support "slicing a slice") but for most purposes might be OK.
To make this sequence r/w you need to add __setitem__
, __delitem__
, and insert
:
class ListSlice(ROListSlice):
def __setitem__(self, i, v):
self.alist[self.adj(i)] = v
def __delitem__(self, i, v):
del self.alist[self.adj(i)]
self.alen -= 1
def insert(self, i, v):
self.alist.insert(self.adj(i), v)
self.alen += 1
Perhaps just use a numpy array:
In [19]: import numpy as np
In [20]: l=np.arange(10)
Basic slicing numpy arrays returns a view, not a copy:
In [21]: lv=l[3:6]
In [22]: lv
Out[22]: array([3, 4, 5])
Altering l
affects lv
:
In [23]: l[4]=-1
In [24]: lv
Out[24]: array([ 3, -1, 5])
And altering lv
affects l
:
In [25]: lv[1]=4
In [26]: l
Out[26]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
You can do that by creating your own generator using the original list reference.
l = [1,2,3,4,5]
lv = (l[i] for i in range(1,4))
lv.next() # 2
l[2]=-1
lv.next() # -1
lv.next() # 4
However this being a generator, you can only go through the list once, forwards and it will explode if you remove more elements than you requested with range
.