[Python-ideas] improvements to slicing

Ron Adam rrr at ronadam.com
Wed Oct 6 21:21:03 CEST 2010


On 10/06/2010 08:58 AM, Nick Coghlan wrote:

> If I was going to ask for a change to anything in Python's
> indexing semantics, it would be for negative step values to create
> ranges that were half-open at the beginning rather than the end, such
> that reversing a slice just involved swapping the start value with the
> stop value and negating the step value.

Yes, negative slices are very tricky to get right.  They could use some 
attention I think.

> As it is, you also have to
> subtract one from both the start and stop value to get the original
> range of values back. However, just like the idea of ranges starting
> from 1 rather than 0, the idea of negative slices giving ranges
> half-open at the start rather than the end is also doomed by
> significant problems with backwards compatibility. For a new language,
> you might be able to make the argument that the alternative behaviour
> is a better design choice. For an existing one like Python, any
> possible benefits are so nebulous as to not be worth the inevitable
> hassle involved in changing the semantics)


We don't need to change the current range function/generator to add 
inclusive or closed ranges.  Just add a closed_range() function to the 
itertools or math module.

    [n for n in closed_range(-5, 5, 2)]  --> [-5, -3, -1, 1, 3, 5]



I just noticed the __getslice__ method is no longer on sequences. (?)



My preference is for slicing to be based more on practical terms for 
manipulating sequences rather than be defined in a purely mathematical way.


1. Have the direction determine by the start and stop values rather than 
than by the step value so that the following is true.

    "abcdefg"[start:stop:step] == "abcdefg"[start:stop][::step]


Reversing the slice can be done by simply swapping the start and stop.

Negating the slice too would give you ...

    "abcdefg"[start:stop:step] == "abcdefg"[stop:start:-step]


Negating the step would not always give you the reverse sequence for steps 
larger than 1, because the result may not contain the same values.

 >>> 'abcd'[::2]
'ac'
 >>> 'abcd'[::-2]
'db'

This is the current behavior and wouldn't change.


A positive step value would step from the left, and a negative step value 
would step from the right of the slice determined by start and stop.  This 
already works if you don't give stop and start values.

 >>> "abcdefg"[::2]
'aceg'
 >>> "abcdefg"[::-2]
'geca'


And these can be used in for loops or list comps.

 >>> [c for c in "abcdefg"[::2]]
['a', 'c', 'e', 'g']



If we could add a width value to slices we would be able to do this.

 >>> "abcdefg"[::2:2]
'abcdefg'

;-)


As unimpressive as that looked, when used in a for loop or list comp it 
would give us an easy and useful way to step through data.

    [cc for cc in "abcdefg"[::2:2]]  -->  ['ab', 'cd', 'ef', 'g']


You could also spell that as...

    list("abcdefg")[::2:2])  --> ['ab', 'cd', 'ef', 'g']



The problems start when you try to use actual index values to specify 
start and stop ranges.

You can't index the last element with an explicit stop value.

 >>> "abcdefg"[0:-1]
'abcdef'

 >>> "abcdefg"[0:-0]
''

But we can use "None" which is awkward and requires testing the stop value 
when the index is supplied by a variable.

 >>> 'abcdefg'[:None]
'abcdefg'

I'm not sure how to fix this one. We've been living with this for a long 
time so it's not like we need to fix it all at once.


Negative indexes can be confusing.

 >>> "abcdefg"[-5:5]
'cde'                         # OK
 >>> "abcdefg"[5:-5]
''                            # Expected "edc'" here, not ''.
 >>> "abcdefg"[5:-5:-1]
'fed'                         # Expected reverse of '' here,
                               # or 'cde',  not 'fed'.



With the suggested change we get...

 >>> "abcdefg"[-5:5]
'cde'                          # Stays the same.
 >>> "abcdefg"[5:-5]
'edc'                          # Swapping start and stop reverses it.
 >>> "abcdefg"[5:-5:-1]
'cde'                          # Negating the step, reverses it again.


I think these are easier to use than the current behavior.  It doesn't 
change slices using positive indexes and steps so maybe it's not so 
backward incompatible to sneak in.  ;-)

Ron




















More information about the Python-ideas mailing list