Slicing in depth
Cut lists, strings and tuples with start, stop and step, use negative indexes, and change lists with slice assignment.
What you will learn
- Read any slice of the form [start:stop:step]
- Use negative indexes and steps, including to reverse a sequence
- Copy a list with a slice
- Replace, insert and delete list items with slice assignment
On this page
You have used items[0] to get one element. A slice gets a stretch of them: the first three, the last two, every other one, the whole thing backwards. Slices work the same way on lists, strings, tuples and ranges, and once the rule clicks you will stop reaching for loops to do these jobs.
The slice rule
seq[start:stop:step] takes the items from start up to, but not including, stop, moving by step. This is the same half-open rule as range. Every part is optional.
letters = ["a", "b", "c", "d", "e", "f"]
print(letters[1:4])print(letters[:2], letters[4:])print(letters[::2])['b', 'c', 'd']['a', 'b'] ['e', 'f']['a', 'c', 'e']- Leave out
startand it begins at the start (or the end, when the step is negative). - Leave out
stopand it runs to the end (or to the beginning, for a negative step). - Leave out
stepand it is 1.
Think of the indexes as sitting between the items. In letters[1:4], index 1 is the gap before "b" and index 4 the gap before "e", so you get b, c, d. A handy consequence is that letters[:k] and letters[k:] split the list at k into two parts with nothing lost or repeated.
Negative indexes and steps
Negative numbers count from the end: -1 is the last item, -2 the one before. So letters[-2:] is “the last two” and letters[:-2] is “everything except the last two”. A negative step walks backwards, and [::-1] is the standard way to reverse a sequence.
letters = ["a", "b", "c", "d", "e", "f"]
print(letters[-2:], letters[:-2])print(letters[::-1])print(letters[4:1:-1])['e', 'f'] ['a', 'b', 'c', 'd']['f', 'e', 'd', 'c', 'b', 'a']['e', 'd', 'c']When going backwards, start should be the larger index: letters[4:1:-1] gives e, d, c, stopping before index 1.
If you only need to walk a sequence backwards in a loop, reversed(letters) does it without building a copy. The slice [::-1] is for when you want the reversed sequence itself.
Slices are forgiving. Out-of-range bounds are clipped rather than raising IndexError, so a slice past the end is just empty. A single index like letters[10] is not so lenient.
letters = ["a", "b", "c", "d", "e", "f"]print(letters[10:20])print(letters[4:100])[]['e', 'f']Strings, tuples and copies
Strings and tuples slice too, and give back a string or a tuple.
text = "Python"print(text[1:4], text[::-1])print((10, 20, 30, 40)[1:3])yth nohtyP(20, 30)A slice of a list is a new list, which is why letters[:] is a common way to copy one. Remember that it is a shallow copy, as the lists lesson showed.
letters = ["a", "b", "c"]copy = letters[:]copy[0] = "z"print(letters[0], copy[0])a zA slice can also be named with the built-in slice(start, stop, step) and reused:
letters = ["a", "b", "c", "d", "e", "f"]middle = slice(1, -1)print(letters[middle])['b', 'c', 'd', 'e']Slice assignment
Lists, being mutable, let you assign to a slice and change several items at once. With a plain slice (no step), the replacement can be any length, so the list grows or shrinks to fit.
nums = [0, 1, 2, 3, 4, 5]nums[1:3] = [10, 20, 30]print(nums)[0, 10, 20, 30, 3, 4, 5]Two items became three. del nums[1:3] deletes a stretch, and assigning to an empty slice such as nums[2:2] inserts without removing anything.
nums = ["w", 10, "x", 30, "y"]del nums[1:3]print(nums)nums[2:2] = ["new"]print(nums)['w', 30, 'y']['w', 30, 'new', 'y']With an extended slice (a step other than 1), the sizes must match exactly:
nums = [0, 1, 2, 3, 4, 5]nums[::2] = ["a", "b", "c"]print(nums)['a', 1, 'b', 3, 'c', 5]A replacement of the wrong size gives a ValueError:
nums = [0, 1, 2, 3, 4, 5]nums[::2] = [1]ValueError: attempt to assign sequence of size 1 to extended slice of size 3Common pitfalls
- Forgetting that stop is excluded.
items[0:3]has three items, indexes 0 to 2. - Reversing with a wrong start.
items[1:5:-1]is empty because it walks backwards from a smaller index. Useitems[::-1]or put the larger index first. - Thinking
a = b[:]is a deep copy. It copies one level. Nested lists are still shared. - Expecting slice errors on bad bounds. Slices clip silently, which can hide an off-by-one mistake. Single indexes raise
IndexError. - Trying to assign into a string slice.
text[0:2] = "ab"raisesTypeError, because strings are immutable.
Predict the four lines this prints, then run the file. Input: none. Expected output: four lines.
word = "abcdefgh"print(word[2:5])print(word[-3:])print(word[::3])print(word[6:1:-2])Show solution
cdefghadggecword[2:5] takes indexes 2, 3 and 4. word[-3:] starts three from the end. A step of 3 takes indexes 0, 3 and 6. The last slice walks backwards from index 6 in steps of 2: indexes 6, 4 and 2.
Given data = [1, 2, 3, 4, 5], print the list rotated left by 2, rotated right by 2, rotated left by 7 (larger than the length), a reversed copy, and the original list. Finally reverse data in place and print it.
Input: data = [1, 2, 3, 4, 5]. Expected output:
[3, 4, 5, 1, 2][4, 5, 1, 2, 3][3, 4, 5, 1, 2][5, 4, 3, 2, 1][1, 2, 3, 4, 5][5, 4, 3, 2, 1]Show solution
data = [1, 2, 3, 4, 5]
k = 2print(data[k:] + data[:k])print(data[-k:] + data[:-k])
k = 7 % len(data)print(data[k:] + data[:k])
print(data[::-1])print(data)
data[:] = data[::-1]print(data)[3, 4, 5, 1, 2][4, 5, 1, 2, 3][3, 4, 5, 1, 2][5, 4, 3, 2, 1][1, 2, 3, 4, 5][5, 4, 3, 2, 1]Moving the first k items to the back is data[k:] + data[:k], and 7 % len(data) wraps a big shift back into range. data[::-1] makes a reversed copy and leaves data alone. data[:] = ... writes the result back into the same list, so any other name for that list sees the change.
Split items = [1, 2, 3, 4, 5, 6, 7] into pieces of at most 3 items and print each piece on its own line. Use range with a step to find where each piece starts.
Input: items. Expected output: [1, 2, 3], [4, 5, 6], [7], one per line.
Show solution
items = [1, 2, 3, 4, 5, 6, 7]size = 3
for start in range(0, len(items), size): print(items[start : start + size])[1, 2, 3][4, 5, 6][7]range(0, len(items), size) gives the start of each chunk, and the slice takes size items from there. The last slice is clipped automatically, so the short final chunk needs no special case. The same loop works on a string.
Summary
seq[start:stop:step]excludesstop, accepts negative numbers, and clips out-of-range bounds.seq[::-1]reverses,reversed(seq)walks backwards in a loop, andseq[:]makes a shallow copy of a list.- Assigning to a list slice can replace, insert or delete. An extended slice needs a replacement of equal size.
Next: comprehensions.