0

What is an idiomatic and efficient way of obtaining a iterator equivalent of this slice?

obj[start:stop:step]

functionally equivalent to (I think) something like this:

def iter_slice(obj, start, stop, step=None):
        
    length = len(obj)

    range_step = 1 if step is None else step
    
    range_start = ((0 if range_step > 0 else length - 1) if start is None
                   else start if start >= 0
                   else max(length + start, 0))

    range_stop = ((length if range_step > 0 else -1) if stop is None
                  else min(stop, length) if stop >= 0
                  else length + stop)

    for i in range(range_start, range_stop, range_step):
        yield obj[i]

I am wondering if there is a way that can make use of either Python's builtin slicing capabilities or some library function, rather than have to try to handle all the cases explicitly (as I've attempted above, whether correctly or not).

There is some discussion here, but the answers all involve one of the following:

  • use of itertools.islice, but this doesn't support negative values, and also it is not an efficient way to iterate over a slice with a large start or step where indexing is possible instead

  • explicitly creating a slice in memory in order to iterate over it, rather than iterating over the original object

  • use of some simple expression for the arguments to range, which works for non-negative numbers that are in range, but is not easily generalised to cover all the cases

Is there a concise way that avoids these issues?

alani
  • 12,573
  • 2
  • 13
  • 23
  • The range() function works similarly to slice(). Maybe you can find a way to use that. – Blupper Sep 10 '20 at 07:45
  • @Blupper I couldn't see an obvious *simple* one - in particular, to handle many of the cases (`None` or negative indices, or out-of-range indices) it needs to know about `len(obj)`. – alani Sep 10 '20 at 07:47
  • I'm just a little confused as to what you are doing. Why is slicing not an option? Wouldn't for x in obj[start:stop:step]: work? – Blupper Sep 10 '20 at 07:52
  • 1
    A pure iterator does not support negative indexes since its length is not known. – Klaus D. Sep 10 '20 at 07:55
  • @Blupper If you do `for x in obj[start:stop:step]:` it creates a *copy* of all that data just in order to iterate over it. – alani Sep 10 '20 at 08:18
  • @alani A right. I didn't know you needed to modify the data. In that case, maybe you could create a list of equal length to the original, only containing numbers representing the items index in the list. Then you can use my approach to iterate over it and access the corresponding index in the original list. If your problem is memory efficiency then I'm not sure. – Blupper Sep 10 '20 at 08:31

0 Answers0