Reputation: 89
I am trying to reverse slice of a list in python but it returns an empty list. But when I try with whole list, it works fine. Am I missing anything here?
l = [1,2,3,4,5,6,7,8]
l[::-1] = [8, 7, 6, 5, 4, 3, 2, 1] # <<< This worked fine.
l[2:5] = [3, 4, 5]
l[2:5:-1] = [] # <<< Expecting [5,4,3] here.
Any clues?
Upvotes: 6
Views: 4532
Reputation:
Why does L[::-1]
return a reversed list, but L[2:5:-1]
an empty []
one???
[start:stop:step]
slice a missing start value will always be set to the beginning (the start) and the missing stop value to the end of a list.This assumption is not true because in Python if you don't explicit specify start
and stop
Python will substitute the missing start
and stop
values differently depending on the sign of step
, setting the start
to the end of the list, not the beginning, in case of a negative step
value.
When you don't specify the start
and/or stop
values in a slice L[start:stop:step]
(i.e. use L[::step]
) Python will behave as follows :
step
has a positive value than start
will be set to 0
zero ( and stop
to len(L)
)step
has a negative value than start
will be set to len(L)-1
. Which value will be assigned to stop
if it isn't specified can't be expressed in terms of slicing and would need conversion of slice defining values to parameters of range()
.The different cases of handling not explicitly specified slice values and the feature allowing usage of negative values for indices in slices is coded in Python source code with multiple if
statements which finally define the slice behavior. So to fully understand Python slicing you have to know how they are coded. And because there does not exist one and the only one making sense straightforward rule for coding slice behavior, the chosen set of decisions for designing slices does not always directly meet ones intuitive expectations.
Let's provide some code to prove the above to be true along with another ways of explaining the same as already stated above (see comments in code). The code below uses Pythons assert
statement to check if the given comparisons show the right sliced values and the in Python for slice
objects available .indices()
method giving the parameters for range()
returning the required list indices representing by the slice object:
L = [1,2,3,4,5,6]
assert L[ : : ] == [1,2,3,4,5,6] # << OK. No AssertionError.
assert L[ : :-1] == L[len(L)-1:-len(L)-1:-1] == [6,5,4,3,2,1]
assert L[ : :-1] == [6,5,4,3,2,1] # << OK. No AssertionError.
assert L[2:5: ] == [3,4,5]
# ===
assert L[2: :-1] == [3,2,1]
# ^-- because with -1 step and decreasing i -= 1 starting with 0
# ^-- the condition (2+i > -1) for list index gives [3,2,1]
assert slice(2,None,-1).indices(len(L)) == (2,-1,-1)
# ^-- and because the None becomes -1 ( for use in range() )
# ===
assert L[ :5:-1] == []
# ^-- because with -1 step and decreasing i -= 1 starting with 0
# ^-- no list item will meet the condition (2+i > 5) for its index
assert slice(None,5,-1).indices(len(L)) == (5,5,-1)
# ^-- and because the None becomes 5 ( for use in range() )
# ===
assert L[2:5:-1] == []
# ^-- because with -1 step and decreasing i -= 1 starting with 0
# ^-- no list item will meet the condition (2+i > 5) for its index
assert slice(2,5,-1).indices(len(L)) == (2,5,-1)
# ^-- and because range(2,5,-1) does not return any values
# ^-- and because you are starting at 2 and stepping -1 trying to get
# to 5 ( you can't count backwards from 2 to 5 )
# ===
assert L[2:5][::-1] == [5,4,3] # it works in different square brackets
assert L[4:1:-1] == [5,4,3] # '-1' is not reversing, it is stepping
Upvotes: 0
Reputation: 49318
Slice notation is [start:stop:step]
. This means "begin at start
, then increase by step
until you get to end
." It's similar to this construct:
counter = 0
stop = 10
step = 1
while counter < stop:
print(counter)
counter += step
This will produce, as expected, 0
through 9
.
Now imagine if we tried it with the values you're using:
counter = 2
stop = 5
step = -1
while counter < stop:
print(counter)
counter += step
If we actually executed this, it would print 2
, then add -1
to that to get 1
, then print 1
and add -1
to that to get 0
, then it would be -1
, -2
, and so on, forever. You would have to manually halt execution to stop the infinite loop.
If you leave start
or stop
empty, as in [:4]
or [::-1]
, this indicates the beginning or end of the sequence, as determined by the step
. Python will go forwards with a positive step
and backwards with a negative step
(trying to use a step
of 0
produces an error).
>>> l[2::]
[3, 4, 5, 6, 7, 8]
>>> l[2::-1]
[3, 2, 1]
>>> l[:2:]
[1, 2]
>>> l[:2:-1]
[8, 7, 6, 5, 4]
If you specify a start
, end
, and step
that couldn't work (an empty step
defaults to 1
), Python will simply return an empty sequence.
Upvotes: 0
Reputation: 11968
The syntax is always [start:end:step]
so if you go backwards your start needs to be greater than the end. Also remember that it includes start and excludes end, so you need to subtract 1 after you swap start and end.
l[5:2:-1]= [6, 5, 4]
l[4:1:-1]= [5, 4, 3]
Upvotes: 9