1

I want to use a use a name to refer to an index range for array/list indexing. Plus, I want to be able to do arithmetic on it. For instance, I want to do

myrange = 3:10
myrange2 = myrange + 5
arr2[myrange2] = arr1[myrange]  # This being equivalent to arr2[8:15] = arr1[3:10] 
myrange3 = myrange * 2 + 5
arr2[myrange3] = arr1[myrange]  # This being equivalent to arr2[11:25] = arr1[3:10] 

Is this possible with python lists and numpy arrays?

  • 1
    Looks difficult as slice is not subclassable https://stackoverflow.com/a/39971377/7207392. Also using dunders `__int__` or `__index__` doesn't seem to work. – Paul Panzer Dec 08 '19 at 13:49
  • The 'm:n' slice notation only works in an indexing expression. That is, it's a Python syntax feature. The interpreter converts it to a `slilce` object, and passes that to the `__getitem__` method. By itself a slice object is quite simple, with just a couple of attributes. `np.s_` and `np.r_` are special `numpy` class instances that help generate and expand slices. – hpaulj Dec 08 '19 at 17:50
  • @hpaulj - I will try digesting your comment. In the meantime, would you say the answer to my question is Yes or No? (I could not figure this out from the comment, yet). – sancho.s ReinstateMonicaCellio Dec 09 '19 at 05:13
  • No, you cannot make the Python interpreter evaluate `myrange = 3:10`. That's an `invalid syntax` error. – hpaulj Dec 09 '19 at 05:18
  • @hpaulj - Please see the approach in the answers... they seem to show that it is possible (by not using strictly the notation `3:10`, but being able to name a slice)... – sancho.s ReinstateMonicaCellio Dec 09 '19 at 05:24
  • Look at `numpy.lib.index_tricks.py` to see some games that `numpy` plays with slices. It defines several classes that have their own `__getitem__` methods. This allows them to use the `n:m:s` syntax. One even allows a `complex` step value, `np.r_[0:10:11j]` is evaluated as `np.linspace(0,10,11)`. – hpaulj Dec 09 '19 at 06:10

2 Answers2

2

You can simply use a slice():

myrange = slice(3, 10)
myrange2 = slice(3 + 5, 10 + 5)
arr2[myrange2] = arr1[myrange]

This can be made into a function easily:

def add_to_slice(slice_, offset):
    return slice(slice_.start + offset, slice_.stop + offset)


myrange = slice(3, 10)
myrange2 = add_to_slice(myrange, 5)
arr2[myrange2] = arr1[myrange]

If you want to use + you would need to patch the slice class (since slice is not an acceptable base class) to include a __add__ method, i.e.:

class Slice(slice):
    pass

TypeError: type 'slice' is not an acceptable base type


EDIT

A "perhaps close enough" approach would be wrap a slice around another class, say Slice and, optionally, make it easy to access the inner slice, e.g.:

class Slice(object):
    def __init__(self, *args):
        self.slice_ = slice(*args)

    def __call__(self):
        return self.slice_

    def __add__(self, offset):
        return Slice(self.slice_.start + offset, self.slice_.stop + offset, self.slice_.step)


myrange = Slice(2, 5)
myrange2 = myrange + 3

mylist = list(range(100))
print(mylist[myrange()])
print(mylist[myrange2()])

(You may want to refine the code for production to handle the possibility of the attributes of slice being None).

norok2
  • 25,683
  • 4
  • 73
  • 99
  • Very interesting approach. I will try this out. I meant to be able to do other arithmetic operations as well, and even compound operations (e.g., `slice * 2 + 4`). With this approach, it seems to me that is also straightforward, by defining `__multiply__`, etc. Would you think so? – sancho.s ReinstateMonicaCellio Dec 09 '19 at 05:14
  • @sancho.sReinstateMonica Yes, you can peek the [`operator`](https://docs.python.org/3/library/operator.html) module to see what to implement (e.g. `*` operator is `__mul__`, etc.). Note that you could also accept a `str` in the constructor with the *colon* syntax, which would allow you to write `Slice('1:10')` in place of `Slice(1, 10)`, although that may be useful for handling user input than to simplify syntax in the code. – norok2 Dec 09 '19 at 08:48
1

You can use slice

a = slice(1,3)
'hello'[a] -> ell

You can access the attributes with a.start/a.stop/a.step. The attributes are read only but you can create a new slice from them for the arithmetic requirement

Ron Serruya
  • 3,988
  • 1
  • 16
  • 26