Let's talk about slicing lists in Python.
Let's say we have a fruits
variable that points to a list:
>>> fruits = ['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
>>> fruits
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
We can get an item from this list by indexing it:
>>> fruits[3]
'kiwi'
If we put a colon and another number inside the square brackets, we'll slice this list instead of indexing it:
>>> fruits[0:3]
['watermelon', 'apple', 'lime']
Slicing a list gives us back a new list. We're getting a list of the first three items within our original list.
Note that the original list is unchanged:
>>> fruits
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
Slicing takes a portion of the elements from our original list and makes a new list out of them:
>>> first3 = fruits[0:3]
>>> first3
['watermelon', 'apple', 'lime']
>>> fruits
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
What do you think we might get if we were to slice our list from 1
to 3
instead of from 0
to 3
?
>>> fruits[1:3]
Here are some guesses:
What's your guess?
When we slice with 1
and 3
, we only get 2 items this time:
>>> fruits[1:3]
['apple', 'lime']
>>> fruits
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
With Python's slicing syntax, the first item is the start index, and the second item is the stop index. The start index is inclusive, but the stop index is exclusive, meaning Python stops just before the stop index.
So we got the items at index 1
and index 2
because we stopped just before index 3
.
The start and stop values are both optional when slicing.
If we don't supply a start index, we'll start at the beginning of the list (index 0
):
>>> fruits[:3]
['watermelon', 'apple', 'lime']
So that gave us the first 3 items.
If we don't supply a stop index, we'll stop at the end of the list:
>>> fruits[1:]
['apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
So this slice gave us everything from the second item onward.
You may be wondering, why is the start index included, but the stop index is excluded?
The exclusivity of the stop index actually has a nice side effect: if you slice up to index 3 and then start slicing again from 3 onward, those two slices won't overlap.
>>> fruits[:3]
['watermelon', 'apple', 'lime']
>>> fruits[3:]
['kiwi', 'pear', 'lemon', 'orange']
So if we concatenate those slices back together, we'd get back something equivalent to our original list:
>>> fruits[:3] + fruits[3:]
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
>>> fruits
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
Here's an example of making a new list that moves the first item to the end:
>>> fruits[1:] + fruits[:1]
['apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange', 'watermelon']
We're slicing from the second item (index 1
) onward and then we're slicing up to, but not including, the second item.
This made a new list with the first item (watermelon
) moved to the end of the list.
Note that negative indexes work the same way with slicing as with indexing.
Index -3
would give us the third to last item in a list:
>>> fruits[-3]
'pear'
>>> fruits
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
Slicing from -3
onward would give us the last 3 items in the list:
>>> fruits[-3:]
['pear', 'lemon', 'orange']
Slicing up to index -3
(but not including it) would give us everything except for the last three items in the list:
>>> fruits[-3:]
['pear', 'lemon', 'orange']
Indexing and slicing are a little bit different in the way they treat indexes.
If we index past the end of a list, we'll see a traceback:
>>> fruits[15]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
Indexing out-of-bounds will raise an exception. But slicing past the end of a list doesn't raise an exception:
>>> fruits[:15]
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
An out-of-bounds slice just stops at the end of the list. The same applies to the beginning of the list:
>>> fruits[-3:]
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
This might look like a bug, but it can actually be a helpful feature. For example, if we wanted to get the first 3 items from a list, we could slice it:
>>> first_3_fruits = fruits[:3]
['watermelon', 'apple', 'lime']
And if there are fewer than 3 items in the list we're working with, we'll still get items! We'll just get every item that's in the list:
>>> colors = ["purple", "blue"]
>>> first_3_colors = colors[:3]
['purple', 'blue']
Slices have a start index and a stop index, but they also have an optional step value.
The start index defaults to 0
, the stop index defaults to the end of the list, and the optional step value, defaults to 1
:
>>> fruits[::1]
['watermelon', 'apple', 'lime', 'kiwi', 'pear', 'lemon', 'orange']
If we change the step value to 2, we'll skip every other item:
>>> fruits[::2]
['watermelon', 'lime', 'pear', 'orange']
If we change the step value to 3, it'll show us every third item:
>>> fruits[::3]
['watermelon', 'kiwi', 'orange']
The most common step value to see in a slice is -1
.
A step value of -1
reverses the list:
>>> fruits[::-1]
['orange', 'lemon', 'pear', 'kiwi', 'lime', 'apple', 'watermelon']
Whenever a negative step value is given, the default meaning of start and stop change. With a negative step value, the start value will default to the last item in the list and the stop value will default to just before the beginning of the list.
Reversing a list is the most common use for the step value when slicing, though I typically prefer to use Python's built-in reversed
function instead:
>>> list(reversed(fruits))
['orange', 'lemon', 'pear', 'kiwi', 'lime', 'apple', 'watermelon']
It's important to note that slicing doesn't just work on lists. If we can index an object, we can probably slice it.
For example, we could use slicing to get the last 3 characters in a string:
>>> greeting = "Hello!"
>>> greeting[-3:]
'lo!'
You can slice pretty much any sequence in Python.
A sequence is an object that can be indexed (from 0
to len(sequence)-1
).
Lists, tuples, and strings are all examples of sequences in Python.
The most common uses of slicing in Python are...
Getting the first few items in a sequence
>>> first3 = fruits[:3]
Getting the last few items in a sequence:
>>> last3 = fruits[-3:]
Or getting all items except for the first item, or all items except for the last item:
>>> all_but_first = fruits[1:]
>>> all_but_last = fruits[:-1]
Those are not the only uses of slicing, but they are the most common.
You can use slicing in Python to get portions of a list or any other sequence. Slicing is particularly great for getting the first few items, the last few items, everything but the first item, everything but the last item, or even reversing all the items in a sequence.
Intro to Python courses often skip over some fundamental Python concepts.
Sign up below and I'll explain concepts that new Python programmers often overlook.
Intro to Python courses often skip over some fundamental Python concepts.
Sign up below and I'll share ideas new Pythonistas often overlook.