Support arbitrary type of object as a slice key, as in X[range(0, 4, 2)] meaning X[0 : 4 : 2]

The X.__getitem__(key) allows key to be a slice if supported by the X class. key can be an arbitrary class with a key.__index__() method. But if key is, for example, a range object, that doesn’t work.
I propose a new special method __slice__(self) which returns a slice object (or anything else that has a __slice__() method. This will supply the value of key.__slice() as the key in X[key].
For example:

l = list(range(11)
class S:
   def __slice__(self): return slice(1, 9, 3)
l[S()]
>>> [1, 4, 7]
l[range(1, 20, 3)   # if range has a __slice__() method
>>> [1, 4, 7, 10]

In some of my own code I have something like this:

class Interval:
    def __init__(self, start, stop):
        self.start, self.stop = start, stop
    def applyto(self, container):
        return container[self.start ; self.stop]
    def __slice__(self):
        return slice(self.start, self.stop)
I = Interval(2, 10)
L = list(range(100))

I.applyto(L)

>>>
[2, 3, 4, 5, 6, 7, 8, 9]

I would like to be able to say L[I], but I can only say I.applyto(L)

Obviously, this would be a lot easier to understand.

If an object has both a __index__ and a __slice__ method, I suppose that this should be a TypeError, but this could be up for discussion.

Looking for an adopter

I don’t plan to follow this forum to see if it gets any traction. I only want to put this idea out there and get on with other things.
So someone should please take on the task of posting an Issue with this suggestion if it appears to be popular enough.

If it’s not important enough to you to follow up on it, why should anyone else put in the work?

5 Likes

You can subclass list with a custom __getitem__ that normalizes Interval instances to slices before calling the original __getitem__:

class List(list):
    def __getitem__(self, index):
        return super().__getitem__(
            slice(index.start, index.stop)
            if isinstance(index, Interval)
            else index
        )

class Interval:
    def __init__(self, start, stop):
        self.start, self.stop = start, stop

I = Interval(2, 10)
L = List(range(100))
print(L[I]) # [2, 3, 4, 5, 6, 7, 8, 9]
1 Like

A more general idea to associate multiple indices with a given object (where it has a meaning, of course) would be nice. However __getitem__ accepts slices as the only form of multiple indices. Slices have just one ‘step’ value and having an uniform step is a huge restriction. I’m afraid there aren’t enough suitable types and use-cases. Writing X[range(0,4,2)] instead of X[slice(0,4,2)] is not an improvement, IMO.

2 Likes

Only list’s __getitem__ has this limitation. Aside from some rare occasions where an exact list object has to be used one can almost always use an object of a list subclass with __getitem__ overridden to accept any data type meaningful and/or convenient for the purpose.

1 Like