Can I create a “view” on a Python list?
up vote
38
down vote
favorite
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 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 look 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 like pointer arithmetic in C.
int lv = l + 3;
python c arrays list
add a comment |
up vote
38
down vote
favorite
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 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 look 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 like pointer arithmetic in C.
int lv = l + 3;
python c arrays list
1
For anybody that comes across this question like I did, memoryview now provides this capability.
– robert
Nov 19 '14 at 16:53
@robert How? Thememoryview
works only for objects with buffer interface and list is not one of them.
– zegkljan
Dec 7 '14 at 14:08
In the example provided here you should use abytearray
instead of a list. You may also wrap the list inbytearray
.
– robert
Dec 7 '14 at 14:11
The buffer protocol, since thememoryview
docs don't link to it.
– Kevin J. Chase
Feb 23 '15 at 16:18
add a comment |
up vote
38
down vote
favorite
up vote
38
down vote
favorite
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 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 look 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 like pointer arithmetic in C.
int lv = l + 3;
python c arrays 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 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 look 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 like pointer arithmetic in C.
int lv = l + 3;
python c arrays list
python c arrays list
asked Aug 14 '10 at 23:05
Wai Yip Tung
11.9k72945
11.9k72945
1
For anybody that comes across this question like I did, memoryview now provides this capability.
– robert
Nov 19 '14 at 16:53
@robert How? Thememoryview
works only for objects with buffer interface and list is not one of them.
– zegkljan
Dec 7 '14 at 14:08
In the example provided here you should use abytearray
instead of a list. You may also wrap the list inbytearray
.
– robert
Dec 7 '14 at 14:11
The buffer protocol, since thememoryview
docs don't link to it.
– Kevin J. Chase
Feb 23 '15 at 16:18
add a comment |
1
For anybody that comes across this question like I did, memoryview now provides this capability.
– robert
Nov 19 '14 at 16:53
@robert How? Thememoryview
works only for objects with buffer interface and list is not one of them.
– zegkljan
Dec 7 '14 at 14:08
In the example provided here you should use abytearray
instead of a list. You may also wrap the list inbytearray
.
– robert
Dec 7 '14 at 14:11
The buffer protocol, since thememoryview
docs don't link to it.
– Kevin J. Chase
Feb 23 '15 at 16:18
1
1
For anybody that comes across this question like I did, memoryview now provides this capability.
– robert
Nov 19 '14 at 16:53
For anybody that comes across this question like I did, memoryview now provides this capability.
– robert
Nov 19 '14 at 16:53
@robert How? The
memoryview
works only for objects with buffer interface and list is not one of them.– zegkljan
Dec 7 '14 at 14:08
@robert How? The
memoryview
works only for objects with buffer interface and list is not one of them.– zegkljan
Dec 7 '14 at 14:08
In the example provided here you should use a
bytearray
instead of a list. You may also wrap the list in bytearray
.– robert
Dec 7 '14 at 14:11
In the example provided here you should use a
bytearray
instead of a list. You may also wrap the list in bytearray
.– robert
Dec 7 '14 at 14:11
The buffer protocol, since the
memoryview
docs don't link to it.– Kevin J. Chase
Feb 23 '15 at 16:18
The buffer protocol, since the
memoryview
docs don't link to it.– Kevin J. Chase
Feb 23 '15 at 16:18
add a comment |
9 Answers
9
active
oldest
votes
up vote
30
down vote
accepted
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
Could you do something likedef __slice__(self, *args, **kwargs): return (self.alist[self.start:self.start+self.alen]).__slice__(*args, **kwargs)
to support things like slicing? Basically passing through the request to a slice created on-demand.
– Amber
Aug 14 '10 at 23:34
3
But if you doalist.insert(0, something)
the slice moves! That might or might not be a problem ...
– Jochen Ritzel
Aug 15 '10 at 0:06
@Amber,__slice__
is not a special method in Python. Slicing results in calls to__getindex__
,__setindex__
,__delindex__
, so you'd have to typecheck and adjust that (easier for the getting, as your approach will delegate things OK -- harder for setting and deleting, though).
– Alex Martelli
Aug 15 '10 at 0:35
1
@Alex: Hm. I could have sworn that there were ways to override slicing (say, to allow for things like 2-dimensional slicing). But I could be wrong. :)
– Amber
Aug 15 '10 at 1:06
1
@Amber, of course you can "override slicing" -- you do that by overriding__getitem__
(and maybe the set and del ones as well, for a type with mutable instances), and type-checking / type-switching on the "index" argument (e.g., to allowa[1:2,3:4]
, you deal with receiving, as the "index" argument, a tuple with two items, both of them slice objects).
– Alex Martelli
Aug 15 '10 at 1:31
|
show 5 more comments
up vote
28
down vote
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])
add a comment |
up vote
5
down vote
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
.
add a comment |
up vote
1
down vote
Edit: The object argument must be an object that supports the buffer call interface (such as strings, arrays, and buffers).
- so no, sadly.
I think buffer type is what you are looking for.
Pasting example from linked page:
>>> s = bytearray(1000000) # a million zeroed bytes
>>> t = buffer(s, 1) # slice cuts off the first byte
>>> s[1] = 5 # set the second element in s
>>> t[0] # which is now also the first element in t!
'x05'
there is nobuffer()
builtin in Python 3.memoryview()
could be used instead.
– jfs
Feb 18 '15 at 2:58
Also, this inspects the in memory bytes of the area - Python lists do contain objects (which 'in memory' are pointer to the objects ) so - definetelly, this would be a very wrong approach - One would have to usectypes
, and redo all the Pointer indirection work, as if he was coding in C, that Python does for free
– jsbueno
May 17 '16 at 14:12
add a comment |
up vote
1
down vote
As soon as you will take a slice from a list, you will be creating a new list. Ok, it will contain same objects so as long as objects of the list are concerned it would be the same, but if you modify a slice the original list is unchanged.
If you really want to create a modifiable view, you could imagine a new class based on collection.MutableSequence
This could be a starting point for a full featured sub list - it correctly processes slice indexes, but at least is lacking specification for negative indexes processing:
class Sublist(collections.MutableSequence):
def __init__(self, ls, beg, end):
self.ls = ls
self.beg = beg
self.end = end
def __getitem__(self, i):
self._valid(i)
return self.ls[self._newindex(i)]
def __delitem__(self, i):
self._valid(i)
del self.ls[self._newindex(i)]
def insert(self, i, x):
self._valid(i)
self.ls.insert(i+ self.beg, x)
def __len__(self):
return self.end - self.beg
def __setitem__(self, i, x):
self.ls[self._newindex(i)] = x
def _valid(self, i):
if isinstance(i, slice):
self._valid(i.start)
self._valid(i.stop)
elif isinstance(i, int):
if i<0 or i>=self.__len__():
raise IndexError()
else:
raise TypeError()
def _newindex(self, i):
if isinstance(i, slice):
return slice(self.beg + i.start, self.beg + i.stop, i.step)
else:
return i + self.beg
Example:
>>> a = list(range(10))
>>> s = Sublist(a, 3, 8)
>>> s[2:4]
[5, 6]
>>> s[2] = 15
>>> a
[0, 1, 2, 3, 4, 15, 6, 7, 8, 9]
This is a direct answer to another question that was closed as a duplicate from this one. As other answers from here were relevant, I prefered add it here
– Serge Ballesta
Dec 18 '15 at 17:18
add a comment |
up vote
1
down vote
Subclass the more_itertools.SequenceView
to affect views by mutating sequences and vice versa.
Code
import more_itertools as mit
class SequenceView(mit.SequenceView):
"""Overload assignments in views."""
def __setitem__(self, index, item):
self._target[index] = item
Demo
>>> seq = list(range(10))
>>> view = SequenceView(seq)
>>> view
SequenceView([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> # Mutate Sequence -> Affect View
>>> seq[6] = -1
>>> view[5:8]
[5, -1, 7]
>>> # Mutate View -> Affect Sequence
>>> view[5] = -2
>>> seq[5:8]
[-2, -1, 7]
more_itertools
is a third-party library. Install via > pip install more_itertools
.
1
plus one for making me discovermore_itertools
, though I won't be using your code
– loxaxs
Nov 11 at 14:08
No worries.more_itertools
is a great toolbox. I encourage people to explore it.
– pylang
Nov 12 at 16:59
add a comment |
up vote
1
down vote
https://gist.github.com/mathieucaroff/0cf094325fb5294fb54c6a577f05a2c1
Above link is a solution based on python 3 range ability to be sliced and
indexed in constant time.
It supports slicing, equality comparsion, string casting (__str__
), and
reproducers (__repr__
), but doesn't support assigment.
Creating a SliceableSequenceView of a SliceableSequenceView won't slow down
access times as this case is detected.
sequenceView.py
# stackoverflow.com/q/3485475/can-i-create-a-view-on-a-python-list
try:
from collections.abc import Sequence
except ImportError:
from collections import Sequence # pylint: disable=no-name-in-module
class SliceableSequenceView(Sequence):
"""
A read-only sequence which allows slicing without copying the viewed list.
Supports negative indexes.
Usage:
li = list(range(100))
s = SliceableSequenceView(li)
u = SliceableSequenceView(li, slice(1,7,2))
v = s[1:7:2]
w = s[-99:-93:2]
li[1] += 10
assert li[1:7:2] == list(u) == list(v) == list(w)
"""
__slots__ = "seq range".split()
def __init__(self, seq, sliced=None):
"""
Accept any sequence (such as lists, strings or ranges).
"""
if sliced is None:
sliced = slice(len(seq))
l = looksSliceable = True
l = l and hasattr(seq, "seq") and isinstance(seq.seq, Sequence)
l = l and hasattr(seq, "range") and isinstance(seq.range, range)
looksSliceable = l
if looksSliceable:
self.seq = seq.seq
self.range = seq.range[sliced]
else:
self.seq = seq
self.range = range(len(seq))[sliced]
def __len__(self):
return len(self.range)
def __getitem__(self, i):
if isinstance(i, slice):
return SliceableSequenceView(self.seq, i)
return self.seq[self.range[i]]
def __str__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return str(self.seq[s])
def __repr__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return "SliceableSequenceView({!r})".format(self.seq[s])
def equal(self, otherSequence):
if self is otherSequence:
return True
if len(self) != len(otherSequence):
return False
for v, w in zip(self, otherSequence):
if v != w:
print(v, w)
return False
return True
add a comment |
up vote
0
down vote
You could edit: not do something like
shiftedlist = type('ShiftedList',
(list,),
{"__getitem__": lambda self, i: list.__getitem__(self, i + 3)}
)([1, 2, 3, 4, 5, 6])
Being essentially a one-liner, it's not very Pythonic, but that's the basic gist.
edit: I've belatedly realized that this doesn't work because list()
will essentially do a shallow copy of the list it's passed. So this will end up being more or less the same as just slicing the list. Actually less, due to a missing override of __len__
. You'll need to use a proxy class; see Mr. Martelli's answer for the details.
add a comment |
up vote
0
down vote
If you are going to be accessing the "view" sequentially then you can just use itertools.islice(..)You can see the documentation for more info.
l = [1, 2, 3, 4, 5]
d = [1:3] #[2, 3]
d = itertools.islice(2, 3) # iterator yielding -> 2, 3
You can't access individual elements to change them in the slice and if you do change the list you have to re-call isclice(..).
add a comment |
9 Answers
9
active
oldest
votes
9 Answers
9
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
30
down vote
accepted
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
Could you do something likedef __slice__(self, *args, **kwargs): return (self.alist[self.start:self.start+self.alen]).__slice__(*args, **kwargs)
to support things like slicing? Basically passing through the request to a slice created on-demand.
– Amber
Aug 14 '10 at 23:34
3
But if you doalist.insert(0, something)
the slice moves! That might or might not be a problem ...
– Jochen Ritzel
Aug 15 '10 at 0:06
@Amber,__slice__
is not a special method in Python. Slicing results in calls to__getindex__
,__setindex__
,__delindex__
, so you'd have to typecheck and adjust that (easier for the getting, as your approach will delegate things OK -- harder for setting and deleting, though).
– Alex Martelli
Aug 15 '10 at 0:35
1
@Alex: Hm. I could have sworn that there were ways to override slicing (say, to allow for things like 2-dimensional slicing). But I could be wrong. :)
– Amber
Aug 15 '10 at 1:06
1
@Amber, of course you can "override slicing" -- you do that by overriding__getitem__
(and maybe the set and del ones as well, for a type with mutable instances), and type-checking / type-switching on the "index" argument (e.g., to allowa[1:2,3:4]
, you deal with receiving, as the "index" argument, a tuple with two items, both of them slice objects).
– Alex Martelli
Aug 15 '10 at 1:31
|
show 5 more comments
up vote
30
down vote
accepted
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
Could you do something likedef __slice__(self, *args, **kwargs): return (self.alist[self.start:self.start+self.alen]).__slice__(*args, **kwargs)
to support things like slicing? Basically passing through the request to a slice created on-demand.
– Amber
Aug 14 '10 at 23:34
3
But if you doalist.insert(0, something)
the slice moves! That might or might not be a problem ...
– Jochen Ritzel
Aug 15 '10 at 0:06
@Amber,__slice__
is not a special method in Python. Slicing results in calls to__getindex__
,__setindex__
,__delindex__
, so you'd have to typecheck and adjust that (easier for the getting, as your approach will delegate things OK -- harder for setting and deleting, though).
– Alex Martelli
Aug 15 '10 at 0:35
1
@Alex: Hm. I could have sworn that there were ways to override slicing (say, to allow for things like 2-dimensional slicing). But I could be wrong. :)
– Amber
Aug 15 '10 at 1:06
1
@Amber, of course you can "override slicing" -- you do that by overriding__getitem__
(and maybe the set and del ones as well, for a type with mutable instances), and type-checking / type-switching on the "index" argument (e.g., to allowa[1:2,3:4]
, you deal with receiving, as the "index" argument, a tuple with two items, both of them slice objects).
– Alex Martelli
Aug 15 '10 at 1:31
|
show 5 more comments
up vote
30
down vote
accepted
up vote
30
down vote
accepted
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
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
edited May 2 '15 at 2:15
cpburnz
8,9662074101
8,9662074101
answered Aug 14 '10 at 23:11
Alex Martelli
614k12410311277
614k12410311277
Could you do something likedef __slice__(self, *args, **kwargs): return (self.alist[self.start:self.start+self.alen]).__slice__(*args, **kwargs)
to support things like slicing? Basically passing through the request to a slice created on-demand.
– Amber
Aug 14 '10 at 23:34
3
But if you doalist.insert(0, something)
the slice moves! That might or might not be a problem ...
– Jochen Ritzel
Aug 15 '10 at 0:06
@Amber,__slice__
is not a special method in Python. Slicing results in calls to__getindex__
,__setindex__
,__delindex__
, so you'd have to typecheck and adjust that (easier for the getting, as your approach will delegate things OK -- harder for setting and deleting, though).
– Alex Martelli
Aug 15 '10 at 0:35
1
@Alex: Hm. I could have sworn that there were ways to override slicing (say, to allow for things like 2-dimensional slicing). But I could be wrong. :)
– Amber
Aug 15 '10 at 1:06
1
@Amber, of course you can "override slicing" -- you do that by overriding__getitem__
(and maybe the set and del ones as well, for a type with mutable instances), and type-checking / type-switching on the "index" argument (e.g., to allowa[1:2,3:4]
, you deal with receiving, as the "index" argument, a tuple with two items, both of them slice objects).
– Alex Martelli
Aug 15 '10 at 1:31
|
show 5 more comments
Could you do something likedef __slice__(self, *args, **kwargs): return (self.alist[self.start:self.start+self.alen]).__slice__(*args, **kwargs)
to support things like slicing? Basically passing through the request to a slice created on-demand.
– Amber
Aug 14 '10 at 23:34
3
But if you doalist.insert(0, something)
the slice moves! That might or might not be a problem ...
– Jochen Ritzel
Aug 15 '10 at 0:06
@Amber,__slice__
is not a special method in Python. Slicing results in calls to__getindex__
,__setindex__
,__delindex__
, so you'd have to typecheck and adjust that (easier for the getting, as your approach will delegate things OK -- harder for setting and deleting, though).
– Alex Martelli
Aug 15 '10 at 0:35
1
@Alex: Hm. I could have sworn that there were ways to override slicing (say, to allow for things like 2-dimensional slicing). But I could be wrong. :)
– Amber
Aug 15 '10 at 1:06
1
@Amber, of course you can "override slicing" -- you do that by overriding__getitem__
(and maybe the set and del ones as well, for a type with mutable instances), and type-checking / type-switching on the "index" argument (e.g., to allowa[1:2,3:4]
, you deal with receiving, as the "index" argument, a tuple with two items, both of them slice objects).
– Alex Martelli
Aug 15 '10 at 1:31
Could you do something like
def __slice__(self, *args, **kwargs): return (self.alist[self.start:self.start+self.alen]).__slice__(*args, **kwargs)
to support things like slicing? Basically passing through the request to a slice created on-demand.– Amber
Aug 14 '10 at 23:34
Could you do something like
def __slice__(self, *args, **kwargs): return (self.alist[self.start:self.start+self.alen]).__slice__(*args, **kwargs)
to support things like slicing? Basically passing through the request to a slice created on-demand.– Amber
Aug 14 '10 at 23:34
3
3
But if you do
alist.insert(0, something)
the slice moves! That might or might not be a problem ...– Jochen Ritzel
Aug 15 '10 at 0:06
But if you do
alist.insert(0, something)
the slice moves! That might or might not be a problem ...– Jochen Ritzel
Aug 15 '10 at 0:06
@Amber,
__slice__
is not a special method in Python. Slicing results in calls to __getindex__
, __setindex__
, __delindex__
, so you'd have to typecheck and adjust that (easier for the getting, as your approach will delegate things OK -- harder for setting and deleting, though).– Alex Martelli
Aug 15 '10 at 0:35
@Amber,
__slice__
is not a special method in Python. Slicing results in calls to __getindex__
, __setindex__
, __delindex__
, so you'd have to typecheck and adjust that (easier for the getting, as your approach will delegate things OK -- harder for setting and deleting, though).– Alex Martelli
Aug 15 '10 at 0:35
1
1
@Alex: Hm. I could have sworn that there were ways to override slicing (say, to allow for things like 2-dimensional slicing). But I could be wrong. :)
– Amber
Aug 15 '10 at 1:06
@Alex: Hm. I could have sworn that there were ways to override slicing (say, to allow for things like 2-dimensional slicing). But I could be wrong. :)
– Amber
Aug 15 '10 at 1:06
1
1
@Amber, of course you can "override slicing" -- you do that by overriding
__getitem__
(and maybe the set and del ones as well, for a type with mutable instances), and type-checking / type-switching on the "index" argument (e.g., to allow a[1:2,3:4]
, you deal with receiving, as the "index" argument, a tuple with two items, both of them slice objects).– Alex Martelli
Aug 15 '10 at 1:31
@Amber, of course you can "override slicing" -- you do that by overriding
__getitem__
(and maybe the set and del ones as well, for a type with mutable instances), and type-checking / type-switching on the "index" argument (e.g., to allow a[1:2,3:4]
, you deal with receiving, as the "index" argument, a tuple with two items, both of them slice objects).– Alex Martelli
Aug 15 '10 at 1:31
|
show 5 more comments
up vote
28
down vote
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])
add a comment |
up vote
28
down vote
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])
add a comment |
up vote
28
down vote
up vote
28
down vote
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])
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])
answered Aug 14 '10 at 23:25
unutbu
538k9911461215
538k9911461215
add a comment |
add a comment |
up vote
5
down vote
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
.
add a comment |
up vote
5
down vote
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
.
add a comment |
up vote
5
down vote
up vote
5
down vote
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
.
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
.
answered Aug 14 '10 at 23:11
viraptor
24.2k673146
24.2k673146
add a comment |
add a comment |
up vote
1
down vote
Edit: The object argument must be an object that supports the buffer call interface (such as strings, arrays, and buffers).
- so no, sadly.
I think buffer type is what you are looking for.
Pasting example from linked page:
>>> s = bytearray(1000000) # a million zeroed bytes
>>> t = buffer(s, 1) # slice cuts off the first byte
>>> s[1] = 5 # set the second element in s
>>> t[0] # which is now also the first element in t!
'x05'
there is nobuffer()
builtin in Python 3.memoryview()
could be used instead.
– jfs
Feb 18 '15 at 2:58
Also, this inspects the in memory bytes of the area - Python lists do contain objects (which 'in memory' are pointer to the objects ) so - definetelly, this would be a very wrong approach - One would have to usectypes
, and redo all the Pointer indirection work, as if he was coding in C, that Python does for free
– jsbueno
May 17 '16 at 14:12
add a comment |
up vote
1
down vote
Edit: The object argument must be an object that supports the buffer call interface (such as strings, arrays, and buffers).
- so no, sadly.
I think buffer type is what you are looking for.
Pasting example from linked page:
>>> s = bytearray(1000000) # a million zeroed bytes
>>> t = buffer(s, 1) # slice cuts off the first byte
>>> s[1] = 5 # set the second element in s
>>> t[0] # which is now also the first element in t!
'x05'
there is nobuffer()
builtin in Python 3.memoryview()
could be used instead.
– jfs
Feb 18 '15 at 2:58
Also, this inspects the in memory bytes of the area - Python lists do contain objects (which 'in memory' are pointer to the objects ) so - definetelly, this would be a very wrong approach - One would have to usectypes
, and redo all the Pointer indirection work, as if he was coding in C, that Python does for free
– jsbueno
May 17 '16 at 14:12
add a comment |
up vote
1
down vote
up vote
1
down vote
Edit: The object argument must be an object that supports the buffer call interface (such as strings, arrays, and buffers).
- so no, sadly.
I think buffer type is what you are looking for.
Pasting example from linked page:
>>> s = bytearray(1000000) # a million zeroed bytes
>>> t = buffer(s, 1) # slice cuts off the first byte
>>> s[1] = 5 # set the second element in s
>>> t[0] # which is now also the first element in t!
'x05'
Edit: The object argument must be an object that supports the buffer call interface (such as strings, arrays, and buffers).
- so no, sadly.
I think buffer type is what you are looking for.
Pasting example from linked page:
>>> s = bytearray(1000000) # a million zeroed bytes
>>> t = buffer(s, 1) # slice cuts off the first byte
>>> s[1] = 5 # set the second element in s
>>> t[0] # which is now also the first element in t!
'x05'
edited May 23 '17 at 10:31
Community♦
11
11
answered Aug 15 '10 at 0:32
cji
5,33221515
5,33221515
there is nobuffer()
builtin in Python 3.memoryview()
could be used instead.
– jfs
Feb 18 '15 at 2:58
Also, this inspects the in memory bytes of the area - Python lists do contain objects (which 'in memory' are pointer to the objects ) so - definetelly, this would be a very wrong approach - One would have to usectypes
, and redo all the Pointer indirection work, as if he was coding in C, that Python does for free
– jsbueno
May 17 '16 at 14:12
add a comment |
there is nobuffer()
builtin in Python 3.memoryview()
could be used instead.
– jfs
Feb 18 '15 at 2:58
Also, this inspects the in memory bytes of the area - Python lists do contain objects (which 'in memory' are pointer to the objects ) so - definetelly, this would be a very wrong approach - One would have to usectypes
, and redo all the Pointer indirection work, as if he was coding in C, that Python does for free
– jsbueno
May 17 '16 at 14:12
there is no
buffer()
builtin in Python 3. memoryview()
could be used instead.– jfs
Feb 18 '15 at 2:58
there is no
buffer()
builtin in Python 3. memoryview()
could be used instead.– jfs
Feb 18 '15 at 2:58
Also, this inspects the in memory bytes of the area - Python lists do contain objects (which 'in memory' are pointer to the objects ) so - definetelly, this would be a very wrong approach - One would have to use
ctypes
, and redo all the Pointer indirection work, as if he was coding in C, that Python does for free– jsbueno
May 17 '16 at 14:12
Also, this inspects the in memory bytes of the area - Python lists do contain objects (which 'in memory' are pointer to the objects ) so - definetelly, this would be a very wrong approach - One would have to use
ctypes
, and redo all the Pointer indirection work, as if he was coding in C, that Python does for free– jsbueno
May 17 '16 at 14:12
add a comment |
up vote
1
down vote
As soon as you will take a slice from a list, you will be creating a new list. Ok, it will contain same objects so as long as objects of the list are concerned it would be the same, but if you modify a slice the original list is unchanged.
If you really want to create a modifiable view, you could imagine a new class based on collection.MutableSequence
This could be a starting point for a full featured sub list - it correctly processes slice indexes, but at least is lacking specification for negative indexes processing:
class Sublist(collections.MutableSequence):
def __init__(self, ls, beg, end):
self.ls = ls
self.beg = beg
self.end = end
def __getitem__(self, i):
self._valid(i)
return self.ls[self._newindex(i)]
def __delitem__(self, i):
self._valid(i)
del self.ls[self._newindex(i)]
def insert(self, i, x):
self._valid(i)
self.ls.insert(i+ self.beg, x)
def __len__(self):
return self.end - self.beg
def __setitem__(self, i, x):
self.ls[self._newindex(i)] = x
def _valid(self, i):
if isinstance(i, slice):
self._valid(i.start)
self._valid(i.stop)
elif isinstance(i, int):
if i<0 or i>=self.__len__():
raise IndexError()
else:
raise TypeError()
def _newindex(self, i):
if isinstance(i, slice):
return slice(self.beg + i.start, self.beg + i.stop, i.step)
else:
return i + self.beg
Example:
>>> a = list(range(10))
>>> s = Sublist(a, 3, 8)
>>> s[2:4]
[5, 6]
>>> s[2] = 15
>>> a
[0, 1, 2, 3, 4, 15, 6, 7, 8, 9]
This is a direct answer to another question that was closed as a duplicate from this one. As other answers from here were relevant, I prefered add it here
– Serge Ballesta
Dec 18 '15 at 17:18
add a comment |
up vote
1
down vote
As soon as you will take a slice from a list, you will be creating a new list. Ok, it will contain same objects so as long as objects of the list are concerned it would be the same, but if you modify a slice the original list is unchanged.
If you really want to create a modifiable view, you could imagine a new class based on collection.MutableSequence
This could be a starting point for a full featured sub list - it correctly processes slice indexes, but at least is lacking specification for negative indexes processing:
class Sublist(collections.MutableSequence):
def __init__(self, ls, beg, end):
self.ls = ls
self.beg = beg
self.end = end
def __getitem__(self, i):
self._valid(i)
return self.ls[self._newindex(i)]
def __delitem__(self, i):
self._valid(i)
del self.ls[self._newindex(i)]
def insert(self, i, x):
self._valid(i)
self.ls.insert(i+ self.beg, x)
def __len__(self):
return self.end - self.beg
def __setitem__(self, i, x):
self.ls[self._newindex(i)] = x
def _valid(self, i):
if isinstance(i, slice):
self._valid(i.start)
self._valid(i.stop)
elif isinstance(i, int):
if i<0 or i>=self.__len__():
raise IndexError()
else:
raise TypeError()
def _newindex(self, i):
if isinstance(i, slice):
return slice(self.beg + i.start, self.beg + i.stop, i.step)
else:
return i + self.beg
Example:
>>> a = list(range(10))
>>> s = Sublist(a, 3, 8)
>>> s[2:4]
[5, 6]
>>> s[2] = 15
>>> a
[0, 1, 2, 3, 4, 15, 6, 7, 8, 9]
This is a direct answer to another question that was closed as a duplicate from this one. As other answers from here were relevant, I prefered add it here
– Serge Ballesta
Dec 18 '15 at 17:18
add a comment |
up vote
1
down vote
up vote
1
down vote
As soon as you will take a slice from a list, you will be creating a new list. Ok, it will contain same objects so as long as objects of the list are concerned it would be the same, but if you modify a slice the original list is unchanged.
If you really want to create a modifiable view, you could imagine a new class based on collection.MutableSequence
This could be a starting point for a full featured sub list - it correctly processes slice indexes, but at least is lacking specification for negative indexes processing:
class Sublist(collections.MutableSequence):
def __init__(self, ls, beg, end):
self.ls = ls
self.beg = beg
self.end = end
def __getitem__(self, i):
self._valid(i)
return self.ls[self._newindex(i)]
def __delitem__(self, i):
self._valid(i)
del self.ls[self._newindex(i)]
def insert(self, i, x):
self._valid(i)
self.ls.insert(i+ self.beg, x)
def __len__(self):
return self.end - self.beg
def __setitem__(self, i, x):
self.ls[self._newindex(i)] = x
def _valid(self, i):
if isinstance(i, slice):
self._valid(i.start)
self._valid(i.stop)
elif isinstance(i, int):
if i<0 or i>=self.__len__():
raise IndexError()
else:
raise TypeError()
def _newindex(self, i):
if isinstance(i, slice):
return slice(self.beg + i.start, self.beg + i.stop, i.step)
else:
return i + self.beg
Example:
>>> a = list(range(10))
>>> s = Sublist(a, 3, 8)
>>> s[2:4]
[5, 6]
>>> s[2] = 15
>>> a
[0, 1, 2, 3, 4, 15, 6, 7, 8, 9]
As soon as you will take a slice from a list, you will be creating a new list. Ok, it will contain same objects so as long as objects of the list are concerned it would be the same, but if you modify a slice the original list is unchanged.
If you really want to create a modifiable view, you could imagine a new class based on collection.MutableSequence
This could be a starting point for a full featured sub list - it correctly processes slice indexes, but at least is lacking specification for negative indexes processing:
class Sublist(collections.MutableSequence):
def __init__(self, ls, beg, end):
self.ls = ls
self.beg = beg
self.end = end
def __getitem__(self, i):
self._valid(i)
return self.ls[self._newindex(i)]
def __delitem__(self, i):
self._valid(i)
del self.ls[self._newindex(i)]
def insert(self, i, x):
self._valid(i)
self.ls.insert(i+ self.beg, x)
def __len__(self):
return self.end - self.beg
def __setitem__(self, i, x):
self.ls[self._newindex(i)] = x
def _valid(self, i):
if isinstance(i, slice):
self._valid(i.start)
self._valid(i.stop)
elif isinstance(i, int):
if i<0 or i>=self.__len__():
raise IndexError()
else:
raise TypeError()
def _newindex(self, i):
if isinstance(i, slice):
return slice(self.beg + i.start, self.beg + i.stop, i.step)
else:
return i + self.beg
Example:
>>> a = list(range(10))
>>> s = Sublist(a, 3, 8)
>>> s[2:4]
[5, 6]
>>> s[2] = 15
>>> a
[0, 1, 2, 3, 4, 15, 6, 7, 8, 9]
answered Dec 18 '15 at 17:16
Serge Ballesta
74.7k956128
74.7k956128
This is a direct answer to another question that was closed as a duplicate from this one. As other answers from here were relevant, I prefered add it here
– Serge Ballesta
Dec 18 '15 at 17:18
add a comment |
This is a direct answer to another question that was closed as a duplicate from this one. As other answers from here were relevant, I prefered add it here
– Serge Ballesta
Dec 18 '15 at 17:18
This is a direct answer to another question that was closed as a duplicate from this one. As other answers from here were relevant, I prefered add it here
– Serge Ballesta
Dec 18 '15 at 17:18
This is a direct answer to another question that was closed as a duplicate from this one. As other answers from here were relevant, I prefered add it here
– Serge Ballesta
Dec 18 '15 at 17:18
add a comment |
up vote
1
down vote
Subclass the more_itertools.SequenceView
to affect views by mutating sequences and vice versa.
Code
import more_itertools as mit
class SequenceView(mit.SequenceView):
"""Overload assignments in views."""
def __setitem__(self, index, item):
self._target[index] = item
Demo
>>> seq = list(range(10))
>>> view = SequenceView(seq)
>>> view
SequenceView([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> # Mutate Sequence -> Affect View
>>> seq[6] = -1
>>> view[5:8]
[5, -1, 7]
>>> # Mutate View -> Affect Sequence
>>> view[5] = -2
>>> seq[5:8]
[-2, -1, 7]
more_itertools
is a third-party library. Install via > pip install more_itertools
.
1
plus one for making me discovermore_itertools
, though I won't be using your code
– loxaxs
Nov 11 at 14:08
No worries.more_itertools
is a great toolbox. I encourage people to explore it.
– pylang
Nov 12 at 16:59
add a comment |
up vote
1
down vote
Subclass the more_itertools.SequenceView
to affect views by mutating sequences and vice versa.
Code
import more_itertools as mit
class SequenceView(mit.SequenceView):
"""Overload assignments in views."""
def __setitem__(self, index, item):
self._target[index] = item
Demo
>>> seq = list(range(10))
>>> view = SequenceView(seq)
>>> view
SequenceView([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> # Mutate Sequence -> Affect View
>>> seq[6] = -1
>>> view[5:8]
[5, -1, 7]
>>> # Mutate View -> Affect Sequence
>>> view[5] = -2
>>> seq[5:8]
[-2, -1, 7]
more_itertools
is a third-party library. Install via > pip install more_itertools
.
1
plus one for making me discovermore_itertools
, though I won't be using your code
– loxaxs
Nov 11 at 14:08
No worries.more_itertools
is a great toolbox. I encourage people to explore it.
– pylang
Nov 12 at 16:59
add a comment |
up vote
1
down vote
up vote
1
down vote
Subclass the more_itertools.SequenceView
to affect views by mutating sequences and vice versa.
Code
import more_itertools as mit
class SequenceView(mit.SequenceView):
"""Overload assignments in views."""
def __setitem__(self, index, item):
self._target[index] = item
Demo
>>> seq = list(range(10))
>>> view = SequenceView(seq)
>>> view
SequenceView([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> # Mutate Sequence -> Affect View
>>> seq[6] = -1
>>> view[5:8]
[5, -1, 7]
>>> # Mutate View -> Affect Sequence
>>> view[5] = -2
>>> seq[5:8]
[-2, -1, 7]
more_itertools
is a third-party library. Install via > pip install more_itertools
.
Subclass the more_itertools.SequenceView
to affect views by mutating sequences and vice versa.
Code
import more_itertools as mit
class SequenceView(mit.SequenceView):
"""Overload assignments in views."""
def __setitem__(self, index, item):
self._target[index] = item
Demo
>>> seq = list(range(10))
>>> view = SequenceView(seq)
>>> view
SequenceView([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> # Mutate Sequence -> Affect View
>>> seq[6] = -1
>>> view[5:8]
[5, -1, 7]
>>> # Mutate View -> Affect Sequence
>>> view[5] = -2
>>> seq[5:8]
[-2, -1, 7]
more_itertools
is a third-party library. Install via > pip install more_itertools
.
answered Feb 14 at 3:21
pylang
12.8k23551
12.8k23551
1
plus one for making me discovermore_itertools
, though I won't be using your code
– loxaxs
Nov 11 at 14:08
No worries.more_itertools
is a great toolbox. I encourage people to explore it.
– pylang
Nov 12 at 16:59
add a comment |
1
plus one for making me discovermore_itertools
, though I won't be using your code
– loxaxs
Nov 11 at 14:08
No worries.more_itertools
is a great toolbox. I encourage people to explore it.
– pylang
Nov 12 at 16:59
1
1
plus one for making me discover
more_itertools
, though I won't be using your code– loxaxs
Nov 11 at 14:08
plus one for making me discover
more_itertools
, though I won't be using your code– loxaxs
Nov 11 at 14:08
No worries.
more_itertools
is a great toolbox. I encourage people to explore it.– pylang
Nov 12 at 16:59
No worries.
more_itertools
is a great toolbox. I encourage people to explore it.– pylang
Nov 12 at 16:59
add a comment |
up vote
1
down vote
https://gist.github.com/mathieucaroff/0cf094325fb5294fb54c6a577f05a2c1
Above link is a solution based on python 3 range ability to be sliced and
indexed in constant time.
It supports slicing, equality comparsion, string casting (__str__
), and
reproducers (__repr__
), but doesn't support assigment.
Creating a SliceableSequenceView of a SliceableSequenceView won't slow down
access times as this case is detected.
sequenceView.py
# stackoverflow.com/q/3485475/can-i-create-a-view-on-a-python-list
try:
from collections.abc import Sequence
except ImportError:
from collections import Sequence # pylint: disable=no-name-in-module
class SliceableSequenceView(Sequence):
"""
A read-only sequence which allows slicing without copying the viewed list.
Supports negative indexes.
Usage:
li = list(range(100))
s = SliceableSequenceView(li)
u = SliceableSequenceView(li, slice(1,7,2))
v = s[1:7:2]
w = s[-99:-93:2]
li[1] += 10
assert li[1:7:2] == list(u) == list(v) == list(w)
"""
__slots__ = "seq range".split()
def __init__(self, seq, sliced=None):
"""
Accept any sequence (such as lists, strings or ranges).
"""
if sliced is None:
sliced = slice(len(seq))
l = looksSliceable = True
l = l and hasattr(seq, "seq") and isinstance(seq.seq, Sequence)
l = l and hasattr(seq, "range") and isinstance(seq.range, range)
looksSliceable = l
if looksSliceable:
self.seq = seq.seq
self.range = seq.range[sliced]
else:
self.seq = seq
self.range = range(len(seq))[sliced]
def __len__(self):
return len(self.range)
def __getitem__(self, i):
if isinstance(i, slice):
return SliceableSequenceView(self.seq, i)
return self.seq[self.range[i]]
def __str__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return str(self.seq[s])
def __repr__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return "SliceableSequenceView({!r})".format(self.seq[s])
def equal(self, otherSequence):
if self is otherSequence:
return True
if len(self) != len(otherSequence):
return False
for v, w in zip(self, otherSequence):
if v != w:
print(v, w)
return False
return True
add a comment |
up vote
1
down vote
https://gist.github.com/mathieucaroff/0cf094325fb5294fb54c6a577f05a2c1
Above link is a solution based on python 3 range ability to be sliced and
indexed in constant time.
It supports slicing, equality comparsion, string casting (__str__
), and
reproducers (__repr__
), but doesn't support assigment.
Creating a SliceableSequenceView of a SliceableSequenceView won't slow down
access times as this case is detected.
sequenceView.py
# stackoverflow.com/q/3485475/can-i-create-a-view-on-a-python-list
try:
from collections.abc import Sequence
except ImportError:
from collections import Sequence # pylint: disable=no-name-in-module
class SliceableSequenceView(Sequence):
"""
A read-only sequence which allows slicing without copying the viewed list.
Supports negative indexes.
Usage:
li = list(range(100))
s = SliceableSequenceView(li)
u = SliceableSequenceView(li, slice(1,7,2))
v = s[1:7:2]
w = s[-99:-93:2]
li[1] += 10
assert li[1:7:2] == list(u) == list(v) == list(w)
"""
__slots__ = "seq range".split()
def __init__(self, seq, sliced=None):
"""
Accept any sequence (such as lists, strings or ranges).
"""
if sliced is None:
sliced = slice(len(seq))
l = looksSliceable = True
l = l and hasattr(seq, "seq") and isinstance(seq.seq, Sequence)
l = l and hasattr(seq, "range") and isinstance(seq.range, range)
looksSliceable = l
if looksSliceable:
self.seq = seq.seq
self.range = seq.range[sliced]
else:
self.seq = seq
self.range = range(len(seq))[sliced]
def __len__(self):
return len(self.range)
def __getitem__(self, i):
if isinstance(i, slice):
return SliceableSequenceView(self.seq, i)
return self.seq[self.range[i]]
def __str__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return str(self.seq[s])
def __repr__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return "SliceableSequenceView({!r})".format(self.seq[s])
def equal(self, otherSequence):
if self is otherSequence:
return True
if len(self) != len(otherSequence):
return False
for v, w in zip(self, otherSequence):
if v != w:
print(v, w)
return False
return True
add a comment |
up vote
1
down vote
up vote
1
down vote
https://gist.github.com/mathieucaroff/0cf094325fb5294fb54c6a577f05a2c1
Above link is a solution based on python 3 range ability to be sliced and
indexed in constant time.
It supports slicing, equality comparsion, string casting (__str__
), and
reproducers (__repr__
), but doesn't support assigment.
Creating a SliceableSequenceView of a SliceableSequenceView won't slow down
access times as this case is detected.
sequenceView.py
# stackoverflow.com/q/3485475/can-i-create-a-view-on-a-python-list
try:
from collections.abc import Sequence
except ImportError:
from collections import Sequence # pylint: disable=no-name-in-module
class SliceableSequenceView(Sequence):
"""
A read-only sequence which allows slicing without copying the viewed list.
Supports negative indexes.
Usage:
li = list(range(100))
s = SliceableSequenceView(li)
u = SliceableSequenceView(li, slice(1,7,2))
v = s[1:7:2]
w = s[-99:-93:2]
li[1] += 10
assert li[1:7:2] == list(u) == list(v) == list(w)
"""
__slots__ = "seq range".split()
def __init__(self, seq, sliced=None):
"""
Accept any sequence (such as lists, strings or ranges).
"""
if sliced is None:
sliced = slice(len(seq))
l = looksSliceable = True
l = l and hasattr(seq, "seq") and isinstance(seq.seq, Sequence)
l = l and hasattr(seq, "range") and isinstance(seq.range, range)
looksSliceable = l
if looksSliceable:
self.seq = seq.seq
self.range = seq.range[sliced]
else:
self.seq = seq
self.range = range(len(seq))[sliced]
def __len__(self):
return len(self.range)
def __getitem__(self, i):
if isinstance(i, slice):
return SliceableSequenceView(self.seq, i)
return self.seq[self.range[i]]
def __str__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return str(self.seq[s])
def __repr__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return "SliceableSequenceView({!r})".format(self.seq[s])
def equal(self, otherSequence):
if self is otherSequence:
return True
if len(self) != len(otherSequence):
return False
for v, w in zip(self, otherSequence):
if v != w:
print(v, w)
return False
return True
https://gist.github.com/mathieucaroff/0cf094325fb5294fb54c6a577f05a2c1
Above link is a solution based on python 3 range ability to be sliced and
indexed in constant time.
It supports slicing, equality comparsion, string casting (__str__
), and
reproducers (__repr__
), but doesn't support assigment.
Creating a SliceableSequenceView of a SliceableSequenceView won't slow down
access times as this case is detected.
sequenceView.py
# stackoverflow.com/q/3485475/can-i-create-a-view-on-a-python-list
try:
from collections.abc import Sequence
except ImportError:
from collections import Sequence # pylint: disable=no-name-in-module
class SliceableSequenceView(Sequence):
"""
A read-only sequence which allows slicing without copying the viewed list.
Supports negative indexes.
Usage:
li = list(range(100))
s = SliceableSequenceView(li)
u = SliceableSequenceView(li, slice(1,7,2))
v = s[1:7:2]
w = s[-99:-93:2]
li[1] += 10
assert li[1:7:2] == list(u) == list(v) == list(w)
"""
__slots__ = "seq range".split()
def __init__(self, seq, sliced=None):
"""
Accept any sequence (such as lists, strings or ranges).
"""
if sliced is None:
sliced = slice(len(seq))
l = looksSliceable = True
l = l and hasattr(seq, "seq") and isinstance(seq.seq, Sequence)
l = l and hasattr(seq, "range") and isinstance(seq.range, range)
looksSliceable = l
if looksSliceable:
self.seq = seq.seq
self.range = seq.range[sliced]
else:
self.seq = seq
self.range = range(len(seq))[sliced]
def __len__(self):
return len(self.range)
def __getitem__(self, i):
if isinstance(i, slice):
return SliceableSequenceView(self.seq, i)
return self.seq[self.range[i]]
def __str__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return str(self.seq[s])
def __repr__(self):
r = self.range
s = slice(r.start, r.stop, r.step)
return "SliceableSequenceView({!r})".format(self.seq[s])
def equal(self, otherSequence):
if self is otherSequence:
return True
if len(self) != len(otherSequence):
return False
for v, w in zip(self, otherSequence):
if v != w:
print(v, w)
return False
return True
edited Nov 17 at 11:09
answered Nov 11 at 20:53
Mathieu CAROFF
3267
3267
add a comment |
add a comment |
up vote
0
down vote
You could edit: not do something like
shiftedlist = type('ShiftedList',
(list,),
{"__getitem__": lambda self, i: list.__getitem__(self, i + 3)}
)([1, 2, 3, 4, 5, 6])
Being essentially a one-liner, it's not very Pythonic, but that's the basic gist.
edit: I've belatedly realized that this doesn't work because list()
will essentially do a shallow copy of the list it's passed. So this will end up being more or less the same as just slicing the list. Actually less, due to a missing override of __len__
. You'll need to use a proxy class; see Mr. Martelli's answer for the details.
add a comment |
up vote
0
down vote
You could edit: not do something like
shiftedlist = type('ShiftedList',
(list,),
{"__getitem__": lambda self, i: list.__getitem__(self, i + 3)}
)([1, 2, 3, 4, 5, 6])
Being essentially a one-liner, it's not very Pythonic, but that's the basic gist.
edit: I've belatedly realized that this doesn't work because list()
will essentially do a shallow copy of the list it's passed. So this will end up being more or less the same as just slicing the list. Actually less, due to a missing override of __len__
. You'll need to use a proxy class; see Mr. Martelli's answer for the details.
add a comment |
up vote
0
down vote
up vote
0
down vote
You could edit: not do something like
shiftedlist = type('ShiftedList',
(list,),
{"__getitem__": lambda self, i: list.__getitem__(self, i + 3)}
)([1, 2, 3, 4, 5, 6])
Being essentially a one-liner, it's not very Pythonic, but that's the basic gist.
edit: I've belatedly realized that this doesn't work because list()
will essentially do a shallow copy of the list it's passed. So this will end up being more or less the same as just slicing the list. Actually less, due to a missing override of __len__
. You'll need to use a proxy class; see Mr. Martelli's answer for the details.
You could edit: not do something like
shiftedlist = type('ShiftedList',
(list,),
{"__getitem__": lambda self, i: list.__getitem__(self, i + 3)}
)([1, 2, 3, 4, 5, 6])
Being essentially a one-liner, it's not very Pythonic, but that's the basic gist.
edit: I've belatedly realized that this doesn't work because list()
will essentially do a shallow copy of the list it's passed. So this will end up being more or less the same as just slicing the list. Actually less, due to a missing override of __len__
. You'll need to use a proxy class; see Mr. Martelli's answer for the details.
edited May 23 '17 at 12:02
Community♦
11
11
answered Aug 14 '10 at 23:16
intuited
15k44873
15k44873
add a comment |
add a comment |
up vote
0
down vote
If you are going to be accessing the "view" sequentially then you can just use itertools.islice(..)You can see the documentation for more info.
l = [1, 2, 3, 4, 5]
d = [1:3] #[2, 3]
d = itertools.islice(2, 3) # iterator yielding -> 2, 3
You can't access individual elements to change them in the slice and if you do change the list you have to re-call isclice(..).
add a comment |
up vote
0
down vote
If you are going to be accessing the "view" sequentially then you can just use itertools.islice(..)You can see the documentation for more info.
l = [1, 2, 3, 4, 5]
d = [1:3] #[2, 3]
d = itertools.islice(2, 3) # iterator yielding -> 2, 3
You can't access individual elements to change them in the slice and if you do change the list you have to re-call isclice(..).
add a comment |
up vote
0
down vote
up vote
0
down vote
If you are going to be accessing the "view" sequentially then you can just use itertools.islice(..)You can see the documentation for more info.
l = [1, 2, 3, 4, 5]
d = [1:3] #[2, 3]
d = itertools.islice(2, 3) # iterator yielding -> 2, 3
You can't access individual elements to change them in the slice and if you do change the list you have to re-call isclice(..).
If you are going to be accessing the "view" sequentially then you can just use itertools.islice(..)You can see the documentation for more info.
l = [1, 2, 3, 4, 5]
d = [1:3] #[2, 3]
d = itertools.islice(2, 3) # iterator yielding -> 2, 3
You can't access individual elements to change them in the slice and if you do change the list you have to re-call isclice(..).
answered Aug 12 '16 at 12:43
Adam Gillessen
1
1
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f3485475%2fcan-i-create-a-view-on-a-python-list%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
For anybody that comes across this question like I did, memoryview now provides this capability.
– robert
Nov 19 '14 at 16:53
@robert How? The
memoryview
works only for objects with buffer interface and list is not one of them.– zegkljan
Dec 7 '14 at 14:08
In the example provided here you should use a
bytearray
instead of a list. You may also wrap the list inbytearray
.– robert
Dec 7 '14 at 14:11
The buffer protocol, since the
memoryview
docs don't link to it.– Kevin J. Chase
Feb 23 '15 at 16:18