List slicing in Python

Share
Copied to clipboard.
Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
5 min. read 6 min. video Python 3.8—3.12

Let's talk about slicing lists in Python.

Getting the first N elements from a list

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.

Slicing makes a new 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']

The start index is inclusive but the stop index is exclusive

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:

  1. Two items starting from the second item
  2. Three items starting from the second item
  3. The same three items as before

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.

Default slice start/stop values

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.

Why the stop index isn't included in slices

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.

Negative indexes work too

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']

Out-of-bounds slicing is allowed

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']

The slice step value

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']

Slicing works on all sequences

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 for slicing 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.

Use slicing to get "slices" of sequences in Python

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.

Concepts Beyond Intro to Python

Intro to Python courses often skip over some fundamental Python concepts.

Sign up below and I'll share ideas new Pythonistas often overlook.