List Slicing
Concepts covered: Slicing
A slice pulls a run of consecutive items out of a list and returns them as a new list. You already know the syntax from strings: daily_rows[start:stop] takes the items from position start up to, but not including, position stop. What comes back is always a list, even when the run holds a single item or none at all.
Every example in this section uses the same list: the number of rows a pipeline loaded on each of 8 days, oldest day first.
| Day | Rows loaded | Index | Negative index |
|---|---|---|---|
| Day 1 | 120 | 0 | -8 |
| Day 2 | 135 | 1 | -7 |
| Day 3 | 98 | 2 | -6 |
| Day 4 | 160 | 3 | -5 |
| Day 5 | 142 | 4 | -4 |
| Day 6 | 155 | 5 | -3 |
| Day 7 | 171 | 6 | -2 |
| Day 8 | 149 | 7 | -1 |
daily_rows = [120, 135, 98, 160, 142, 155, 171, 149]
print(daily_rows[2:5])
print(daily_rows[:3])
print(daily_rows[5:])
print(daily_rows[-3:])
print(daily_rows[1:-1])
print(daily_rows[3:4])
print(daily_rows[3])Output:
[98, 160, 142] [120, 135, 98] [155, 171, 149] [155, 171, 149] [135, 98, 160, 142, 155, 171] [160] 160
Compare the last 2 lines. daily_rows[3:4] is a 1-item list, [160], while daily_rows[3] is the number 160 itself. Code that expects a number and receives a list fails at the next arithmetic step, so pick the form you need. When start and stop both fall inside the list, the slice holds stop - start items: [2:5] always returns 3. When start sits at or after stop, the count bottoms out at 0 and the slice is empty: daily_rows[5:2] returns []. Python raises no error and doesn't walk backwards.
Defaults and negative positions
Leaving out start means from the first item, and leaving out stop means through the last. Negative positions count from the end, as they do for strings, so [-3:] reads as the last 3 items however long the list grows. That matters in a daily job: a report on the most recent 3 days shouldn't hard-code [5:], because tomorrow the list holds 9 items and [5:] returns 4 of them.
A step skips items
daily_rows = [120, 135, 98, 160, 142, 155, 171, 149]
print(daily_rows[::2])
print(daily_rows[1::2])
print(daily_rows[1:6:2])
print(daily_rows[::3])Output:
[120, 98, 142, 171] [135, 160, 155, 149] [135, 160, 155] [120, 160, 171]
The third number is the step. Python takes the item at start and jumps ahead by the step to take the next one, repeating until it reaches stop. With a step of 2, [::2] returns every odd day from 1 to 7. [1::2] returns every even day from 2 to 8. A pipeline uses this to split records into 2 alternating halves, or to sample every 3rd record for a spot check.
Slices do not raise IndexError
Indexing a position past the end raises IndexError. Slicing past the end does not: Python clips start and stop to the edges of the list and returns whatever falls between them, possibly an empty list. daily_rows[5:20] returns the 3 items from position 5 through 7. daily_rows[20:30] returns []. That helps when you want up to N items from a list that might be shorter, and hurts when a short result should have stopped the job, because nothing warns you.
A slice is a separate list
daily_rows = [120, 135, 98, 160, 142, 155, 171, 149]
recent = daily_rows[-3:]
recent.append(0)
print(recent)
print(daily_rows)
print(len(daily_rows))Output:
[155, 171, 149, 0] [120, 135, 98, 160, 142, 155, 171, 149] 8
Appending to recent leaves daily_rows with its original 8 items, because the slice built a new list holding the same values. daily_rows[:], a slice with both ends left out, is the shortest way to get a full separate list that you can change without touching the original.
Predicting a stepped slice
To predict a slice with a step, write out the positions it visits before reading any values. For daily_rows[2:7:2], start at position 2 and add 2 each time. That visits every even position up to 6. The next would be 8, which is past the stop of 7, so the walk ends. Only then look up the values: [98, 142, 171]. Listing positions first catches 2 frequent mistakes: counting the stop position as included, and forgetting that the step jumps over the items in between.
What does this program print?
codes = ["a", "b", "c", "d", "e", "f"]
print(codes[1:5:3])
print(codes[4:2])- ['b', 'c', 'd'] then []
- ['b', 'e'] then ['e', 'd']
- ['b', 'e'] then []
- ['b', 'e'] then an IndexError
About this lesson
This section is part of the Lists: Intermediate lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.