I am building a class which has underpinning it a list of ints, its __getitem__ method is then a reasonably computationally involved function outputting a list of potentially large size. Here is the code:
Class A(list):
def __init__(self, n):
self.li = [i for i in range(0, n)]
super().__init__(self.li)
self._ind = 0
def __getitem__(self, item):
assert (type(item) is int and item >= 0) or type(item) is slice
if type(item) is slice:
start, stop, step = item.start, item.stop, item.step
if stop is None:
stop = len(self)
if step is not None:
for i in range(start, stop, step):
yield self[i]
else:
for i in range(start, stop):
yield self[i]
if item == 0 or item == 1:
return []
out = []
while item != 1:
if super().__getitem__(item):
p = super().__getitem__(item)
else:
out.append(item)
return out
if p not in out:
out.append(p)
item //= p
return out
My code is currently not working as it always returns a generator, and this messes up iterating over an instance of the class. However upon slicing, I do not wish it to do all the computations and store it, as this would make it consume a lot more memory. An example of what I wish to do:
from math import prod
test = A(10**7)
out = 0
for i in test[10**6:10**7]:
out += prod(i)
How can I make slices work efficiently?
Instead of returning a generator, return a view. This is an object that conceptually represents the sequence of elements in question. We can do this by storing a reference to the original A instance, plus a range that encodes the instances. When we index into the view, we can ask the range which original index (or indices) are involved.
Supposing we set up the general structure:
Then our view could look like:
The idea is that if we receive an integer index, we translate it through the
rangeobject into an index for the originalA, and call back to its__getitem__(with an integer this time). If we receive anotherslice, we use it to slice our range into a sub-range, and make another view.Note that your A class should already be iterable, due to inheriting from
list. To make the view iterable (and also get theinoperator, forward and reverse iteration,.indexand.countmethods for free), you can inherit fromcollections.abc.Sequence. You just need a__getitem__and__len__- both easy to implement, as above - and the base class will do the rest (while also advertising toisinstancethat your class has these properties).