negative stride list slices

Tim Hochberg tim.hochberg at ieee.org
Wed Sep 1 19:06:44 EDT 2004


Peter Hansen wrote:
> (Please don't top-post.  It really buggers up the quoting
> and makes discussion hard.)
> 
> Julio Oña wrote:
> 
>> Slice has three arguments, [begin:end:step]
>>
>> when doing s[:-3:-1] you are asking the las to elements of the list in
>> reversed order.
> 
> 
> Uh, yeah.  Okay.  So let's say that part was obvious.  Now
> please explain *which* elements are being listed in reverse
> order, referring to the index value -3 and the elided
> index value.  Presumably one of those refers to the beginning
> or end of the list, while the other one refers to something
> that is referenced as "-3".  Can you point to a diagram or
> description in the documentation or tutorial which actually
> explains this?  Or can you (or anyone) explain it in your
> own words?  Or is everyone who thinks this makes sense just
> pretending to actually understand it?


I've been using extended slicing in Numeric and now numarray for years 
and I even wrote some classes that implement extended slicing, but I 
still often find it confusing to use negative indices in practice. 
However, despite that confusion, I'll give a shot at explaining it.

There are (at least) two issues: what to do with missing indices and 
given all the indices, what does it all mean. I'll start with the first 
issue, since it's simpler. Of the three indices (start, stop and step), 
only start and stop are potentially confusing, since step simply 
defaults to 1. Start defaults to the first element (0) if step is 
greater than zero, otherwise it defaults to the last element (len(s)-1). 
Stop, on the other hand, defaults to one past the edge of the sequence. 
If step is positive, it's one past the far edge (len(s)), otherwise it's 
one past the near edge. One would be tempted to write this as -1, but 
that won't works since that means the end of the sequence, so we'll call 
it (-len(s)-1). In summary the missing indices are supplied as:

start = 0 if (step > 0) else len(s)-1
stop = len(s) is (step > 0) else -len(s)-1
step = 1

On to issue number two. I've always understood extended slicing in 
relation to the range. In Numeric this relation is best expressed using 
Numeric.take, however for general python consumption I'll cast it in 
terms of list comprehensions. Since we've already disposed of how to 
supply the missing parts of the slice, this will only deal with the case 
where all parts are supplied. In addition, if start or stop is less than 
zero, we add len(s) so that all values are nonnegative unless stop was 
-len(s)-1, in which case it ends up as -1. With that wordy preamble, 
here's how I've always understood extended slicing:

s[start:stop:step] ~ [s[i] for i in range(start,stop,step)]

Let me show a few examples:


 >>> s = 'python'

 >>> s[:-3:-1]
'no'
 >>> s[len(s)-1:-3:-1] # Fill in the default values
'no'
 >>> [s[i] for i in range(len(s)-1, len(s)-3,-1)] # equiv to s[:-3:-1]
['n', 'o']

 >>> s[4::]
'on'
 >>> s[4:len(s):1] # Fill in the default values
'on'
 >>> [s[i] for i in range(4,len(s),1)] # equiv to s[4::]
['o', 'n']

 >>> s[4::-1]
'ohtyp'
 >>> s[4:-len(s)-1:-1] # Fill in the default values
'ohtyp'
 >>> [s[i] for i in range(4,-1,-1)] # equiv to s[4::-1]
['o', 'h', 't', 'y', 'p']


Is this confusing? Probably. However, I suspect it's a natural 
consequence of Python's zero based indexing. Zero based indexing is 
great in a lot of ways, but it has some unfortunate corner cases for 
negative indices (another topic) and gets downright odd for negative 
strides. So it goes.

I-hope-I-didn't screw-that-up-too-badly-ly yours,

-tim




More information about the Python-list mailing list