3

I have a variable a=range(0,5) and i need to know why and how a[::-1] this works. The answer i am getting is range(4,-1,-1). Thanks for help.

P.S. It is quite a basic question but as this question is answered in a very structured way, i tried to edit the question and make it more general and useful.

7bStan
  • 160
  • 1
  • 4
  • 13
  • It will give you a new list `b` where the elements are in reverse order of `a`. If `a=[0,1,2,3,4]`, `b=[4,3,2,1,0]` – Ahsanul Haque May 23 '17 at 11:25
  • regardless of what `a` is, `[::-1]` reverses its order. – Ma0 May 23 '17 at 11:26
  • 1
    It goes from end to start in steps of one, i.e. reverses the list. – timgeb May 23 '17 at 11:26
  • @Ev.Kounis no, only if `__getitem__` works like the one from lists. – timgeb May 23 '17 at 11:27
  • @timgeb I meant regardless of which list; the fact that it is `range(0,5)` does not make any difference. But the order gets reversed for strings and tuples as well. – Ma0 May 23 '17 at 11:29

3 Answers3

7

Slicing

  1. Negative numbers for start and stop mean "from the end". It's essianlly equivalent of len-value.
  2. Negative number for step means "in reverse order".
  3. Empty start means 0 i.e. 1st element.
  4. Empty stop means len. Stop parameter is exclusive!

So [::-1] means from 1st element to last element in steps of 1 in reverse order.

  1. If you have [start:stop] it's the same as step=1. So [:-1] it means all but last. again it's the last element exclusive. It's the same as [:-1:] or [0:-1:1].

If you have only start, it returns one element given by the index start. Thus [-1] means last element. Same as [len-1] would.

range

Range also has syntax start,stop,step but the step has a different meaning. Step is added repeatedly starting from start. So you start at 4, and go down by adding -1 until you hit stop, also exclusively. So range(5,0)[::-1] is equivalent to range(4,-1,-1). You can compute it.

Why interpreter says range(0,5)[::-1] => range(4, -1, -1)?

Python interpreter is smart enough to convert a slice of range into another range. This is an optimization, ranges are generators. They are dynamic, i.e. they don't hold all the elements in the memory at once. It the interpreter you are using worked step-by-step, it would have to generate whole list, just to be able to iterate in a reverse order. It's smarter to compute new generator.

How it is done, is explained in detail Łukasz'es answer.

Btw. you can force it to generate a list, and prevent from optimizing:

   range(0,5)[::-1]
=> range(4, -1, -1)
   list(range(0,5))[::-1]
=> [4, 3, 2, 1, 0]
luk32
  • 15,812
  • 38
  • 62
  • Yea I was already editing it. I made a mistake. You are obviously right. – luk32 May 23 '17 at 11:35
  • according to this 'b' should be range(5,0,-1). i.e. it should start from 5 and ends at 0. how this (4,-1,-1) came ? – 7bStan May 23 '17 at 11:45
3

The method you used is called Slicing in Python.

Slicing syntax in python is as follows,

[ <first element to include> : <first element to exclude> : <step> ]

where adding the step part is optional.


Here is a representation of how python list considers positive and negative index.

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5 
  -6  -5  -4  -3  -2  -1

When using,

a = range(10)
# a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

a[10:0:-1] #or
a[-1:-10:-1]
# Output = [9, 8, 7, 6, 5, 4, 3, 2, 1]

Why because, when we give 0/-10 as the second parameter, it excludes the element in the 0/-10th position.

So easy way is to omit the second parameter in slicing. That is,

a[10::-1] #or
a[-1::-1]
# Output = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# Which is what we want.

In order to further simplify, if you omit both start and end value and just give step as -1, it again yields the same result.

a[::-1]
# Output = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# The -1 at step position traverse the element from the last to the beginning

Here is a simple cheat sheet for understanding Slicing,

a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[:]         # a copy of the whole array

a[start:end:step] # start through not past end, by step

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

To understand more about slicing, see this


Hope this helps! :)

bharadhwaj
  • 2,059
  • 22
  • 35
1

compute_slice function is used to convert between existing range object (rangeobject *r) and it's new value. As you can see, it's done as O(1) operation based on start/stop/step values of both range and passed slice.

static PyObject *
compute_slice(rangeobject *r, PyObject *_slice)
{
    PySliceObject *slice = (PySliceObject *) _slice;
    rangeobject *result;
    PyObject *start = NULL, *stop = NULL, *step = NULL;
    PyObject *substart = NULL, *substop = NULL, *substep = NULL;
    int error;

    error = _PySlice_GetLongIndices(slice, r->length, &start, &stop, &step);
    if (error == -1)
        return NULL;

    substep = PyNumber_Multiply(r->step, step);
    if (substep == NULL) goto fail;
    Py_CLEAR(step);

    substart = compute_item(r, start);
    if (substart == NULL) goto fail;
    Py_CLEAR(start);

    substop = compute_item(r, stop);
    if (substop == NULL) goto fail;
    Py_CLEAR(stop);

    result = make_range_object(Py_TYPE(r), substart, substop, substep);
    if (result != NULL) {
        return (PyObject *) result;
    }
fail:
    Py_XDECREF(start);
    Py_XDECREF(stop);
    Py_XDECREF(step);
    Py_XDECREF(substart);
    Py_XDECREF(substop);
    Py_XDECREF(substep);
    return NULL;
}
Łukasz Rogalski
  • 22,092
  • 8
  • 59
  • 93
  • Nice stuff, I though OP's asking how is it equivalent, but I guess the real confusion comes from the cleverness of interpreter and transformation of generator by slicing. – luk32 May 23 '17 at 12:00