tags:

views:

277

answers:

7

Bear with me while I explain my question. Skip down to the bold heading if you already understand extended slice list indexing.

In python, you can index lists using slice notation. Here's an example:

>>> A = list(range(10))
>>> A[0:5]
[0, 1, 2, 3, 4]

You can also include a stride, which acts like a "step":

>>> A[0:5:2]
[0, 2, 4]

The stride is also allowed to be negative, meaning the elements are retrieved in reverse order:

>>> A[5:0:-1]
[5, 4, 3, 2, 1]

But wait! I wanted to see [4, 3, 2, 1, 0]. Oh, I see, I need to decrement the start and end indices:

>>> A[4:-1:-1]
[]

What happened? It's interpreting -1 as being at the end of the array, not the beginning. I know you can achieve this as follows:

>>> A[4::-1]
[4, 3, 2, 1, 0]

But you can't use this in all cases. For example, in a method that's been passed indices.

My question is:

Is there any good pythonic way of using extended slices with negative strides and explicit start and end indices that include the first element of a sequence?

This is what I've come up with so far, but it seems unsatisfying.

>>> A[0:5][::-1]
[4, 3, 2, 1, 0]
A: 
a[4::-1]

Example:

Python 2.6 (r26:66714, Dec  4 2008, 11:34:15) 
[GCC 4.0.1 (Apple Inc. build 5488)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[4:0:-1]
[4, 3, 2, 1]
>>> a[4::-1]
[4, 3, 2, 1, 0]
>>>

The reason is that the second term is interpreted as "while not index ==". Leaving it out is "while index in range".

Charlie Martin
I mentioned that in the question. But you can't use that if you are storing your indices in variables for example.
recursive
Oh. I knew I needed a nap.
Charlie Martin
Yeah, I knew that too. I mean me. I need a nap.
recursive
+1  A: 

I believe that the following doesn't satisfy you:

def getReversedList(aList, end, start, step):
    if step < 0 and start == 0:
         return aList[end::step]
    return aList[end:start:step]

or does it? :-)

Abgan
I had considered that, requiring two separate cases seems like it should be unnecessary. But you've given me an idea...
recursive
+1  A: 

Ok, I think this is probably as good as I will get it. Thanks to Abgan for sparking the idea. This relies on the fact that None in a slice is treated as if it were a missing parameter. Anyone got anything better?

def getReversedList(aList, end, start, step):
    return aList[end:start if start!=-1 else None:step]

edit: check for start==-1, not 0

This is still not ideal, because you're clobbering the usual behavior of -1. It seems the problem here is two overlapping definitions of what's supposed to happen. Whoever wins takes away otherwise valid invocations looking for the other intention.

recursive
Hah, haven't thought about that. Nice solution, worth remembering :-)
Abgan
You could replace `None` by `-len(a) + start`. See http://stackoverflow.com/questions/399067/extended-slice-that-goes-to-beginning-of-sequence-with-negative-stride#400118
J.F. Sebastian
+1  A: 
[ A[b] for b in range(end,start,stride) ]

Slower, however you can use negative indices, so this should work:

[ A[b] for b in range(9, -1, -1) ]

I realize this isn't using slices, but thought I'd offer the solution anyway if using slices specifically for getting the result isn't a priority.

codelogic
A: 

But you can't use that if you are storing your indices in variables for example.

Is this satisfactory?

>>> a = range(10)
>>> start = 0
>>> end = 4
>>> a[4:start-1 if start > 0 else None:-1]
[4, 3, 2, 1, 0]
fivebells
A: 

It is error-prone to change the semantics of start and stop. Use None or -(len(a) + 1) instead of 0 or -1. The semantics is not arbitrary. See Edsger W. Dijkstra's article "Why numbering should start at zero".

>>> a = range(10)
>>> start, stop, step = 4, None, -1

Or

>>> start, stop, step = 4, -(len(a) + 1), -1
>>> a[start:stop:step]
[4, 3, 2, 1, 0]

Or

>>> s = slice(start, stop, step)
>>> a[s]
[4, 3, 2, 1, 0]
J.F. Sebastian
A: 

As you say very few people fully understand everything that you can do with extended slicing, so unless you really need the extra performance I'd do it the "obvious" way:

rev_subset = reversed(data[start:stop])

James Antill