Given that slicing ranges is instantaneous you could make a function that gets list elements from a sliced range of indexes:
def getSlice(L,start,stop=None,step=None):
yield from (L[i] for i in range(len(L))[start:stop:step])
L = [*range(100,200)]
print(*getSlice(L,-10,-15,-2)) # 190 188 186
You could also make a wrapper class of your own to process list subscripts as iterators rather than copies of sublists:
class ListSlice:
def __init__(self,L,indexes=None):
self.content = L
self._indexes = indexes
@property
def indexes(self):
if isinstance(self._indexes,slice):
return range(len(self.content))[self._indexes]
if self._indexes is None:
return range(len(self.content))
return self._indexes
@indexes.setter
def indexes(self,value): self._indexes = value
def __len__(self): return len(self.indexes)
def __iter__(self): yield from (self.content[i] for i in self.indexes)
def __getitem__(self,index):
if isinstance(index,slice):
return ListSlice(self.content,self.indexes[index])
return self.content[self.indexes[index]]
def __setitem__(self,index,value):
i = self.indexes[index]
if isinstance(index,slice):
if isinstance(i,range):
self.content[slice(i.start,i.stop,i.step)] = value
else:
for j,v in zip(i,value): self.content[j] = v
else:
self.content[i] = value
usage:
L = [*range(100,200)]
# subscript slices will be iterators (without copying)
L = ListSlice(L)
print(*L[30:40:2]) # 130 132 134 136 138
print(L[32]) # 132
# view on odd indexes (no copy)
Lodds = ListSlice(L,slice(1,None,2)) # or Lodds = ListSlice(L)[1::2]
print(*Lodds[-3:]) # 195 197 199
# re-slice on sliced list (without stacking overhead)
L39 = Lodds[3:9]
print(*L39) # 107 109 111 113 115 117 # L39 is a new ListSlice on L
# arbitrary subset of indexes
Lfibo = ListSlice(L,[1,2,3,5,8,13,21,34,55,89])
print(*Lfibo) # 101 102 103 105 108 113 121 134 155 189
# dynamically changing slicing/indexes
A = [32,56,4,98,29,15]
sA = ListSlice(A)
sA.indexes = sorted(sA.indexes,key=lambda i:A[i])
print(*sA) # 4 15 29 32 56 98
print(A) # [32, 56, 4, 98, 29, 15]
# slice assignments affect the underlying list (L)
Lodds[:3] = [-1]*3
print(*Lodds[:5]) # -1 -1 -1 107 109
print(*L[:10]) # 100 -1 102 -1 104 -1 106 107 108 109
Note that the indirection through indexes, in Python code, will be considerably slower than using native list slicing (despite the internal memory copy done by list slicing). The only benefit would be in extreme cases where total memory usage matters and the size of slices is enormous. The new list returned by slicing a list will only consume 8 bytes per item, no matter what the list items are. That is because both mutable and immutable datatypes are stored as pointers in a list (for immutables all the pointers reference the same memory for a given value).