Python Slicing Cheat Sheet
1 · The lesson
readA slice takes some of the items from a list, a string, a tuple or another sequence (a collection whose items have positions). It gives you the items as a new sequence of the same type. You write it as seq[start:stop:step]. This page shows each part with examples you can run, and the mistakes people make most often. For an introduction, see Lists and Strings.
1. Start and stop
The item at start is included and the item at stop is not. You can leave out any of the three parts:
| Left out | It means |
|---|---|
start | from the beginning (from the end when the step is negative) |
stop | to the end (to the beginning when the step is negative) |
step | 1: every item |
xs = [0, 1, 2, 3, 4, 5] xs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included) xs[:3] # [0, 1, 2] (the first 3 items) xs[3:] # [3, 4, 5] (from index 3 to the end) xs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list) xs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)
2. Count from the end
A negative number counts from the end: -1 is the last item, and -2 is the item before the last.
xs[-1] # 5 (an index, not a slice: the last item itself) xs[-2:] # [4, 5] (the last 2 items) xs[:-2] # [0, 1, 2, 3] (every item except the last 2) xs[1:-1] # [1, 2, 3, 4] (every item except the first and the last) xs[-4:-1] # [2, 3, 4] (up to, but not including, the last item)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) xs = _AutoMock('xs') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included)\nxs[:3] # [0, 1, 2] (the first 3 items)\nxs[3:] # [3, 4, 5] (from index 3 to the end)\nxs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list)\nxs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
3. Step
The third number is the step: how far to move each time. A step of 2 takes every second item.
xs[::2] # [0, 2, 4] (every second item) xs[1::2] # [1, 3, 5] (every second item, starting at index 1) xs[1:5:2] # [1, 3] (start 1, stop 5, step 2) xs[::3] # [0, 3]
setup added so this can run · re-runs 1 earlier block on this page first
xs = ["alpha", "beta", "gamma"] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included)\nxs[:3] # [0, 1, 2] (the first 3 items)\nxs[3:] # [3, 4, 5] (from index 3 to the end)\nxs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list)\nxs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
4. Backwards: a negative step
A negative step goes from right to left. Now start must be the bigger index.
xs[::-1] # [5, 4, 3, 2, 1, 0] (a reversed copy) xs[::-2] # [5, 3, 1] xs[4:1:-1] # [4, 3, 2] xs[3::-1] # [3, 2, 1, 0] (from index 3 back to the start) xs[:2:-1] # [5, 4, 3] (from the end back to index 2, not including it)
setup added so this can run · re-runs 1 earlier block on this page first
xs = ["alpha", "beta", "gamma"] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included)\nxs[:3] # [0, 1, 2] (the first 3 items)\nxs[3:] # [3, 4, 5] (from index 3 to the end)\nxs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list)\nxs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
5. Strings
Slicing works on strings in the same way. A string cannot change, so you build a new string from slices.
s = "python" s[:2] # 'py' s[-3:] # 'hon' s[::-1] # 'nohtyp' (the string reversed) "level"[::-1] == "level" # True (a palindrome: a word that reads the same backwards) s[0].upper() + s[1:] # 'Python' "report.csv"[:-4] # 'report' ("report.csv".removesuffix(".csv") is clearer) [s[i:i + 2] for i in range(0, len(s), 2)] # ['py', 'th', 'on'] (cuts the string into pieces of 2)
6. Other sequences
A slice gives you the same type you started with. A generator (an object that makes values one at a time, such as (n * n for n in range(9))) has no index, so use itertools.islice() to take part of it.
from itertools import islice (1, 2, 3)[1:] # (2, 3) (a slice of a tuple is a tuple) range(10)[2:8:2] # range(2, 8, 2) (a range slice is a range, not a list) b"hello"[1:3] # b'el' squares = (n * n for n in range(9)) list(islice(squares, 3)) # [0, 1, 4]
A dict and a set have no positions, so you cannot slice them. See the itertools cheat sheet for more on islice().
7. Change a list through a slice
You can assign to a slice of a list. The new part can be longer or shorter than the part it replaces. This only works for lists: strings, tuples and ranges cannot change.
xs = [0, 1, 2, 3, 4, 5] xs[1:3] = [7, 8, 9] # [0, 7, 8, 9, 3, 4, 5] (replaces 2 items with 3 items; the list grows) xs[1:4] = [] # [0, 3, 4, 5] (assigning an empty list removes the items at index 1, 2 and 3) xs[:0] = [9] # [9, 0, 3, 4, 5] (assigning to an empty slice inserts items) del xs[::2] # [0, 4] (del removes every second item)
You can keep a slice in a variable with slice(), which helps when you use the same slice many times:
first_three = slice(0, 3) "Margaret"[first_three] # 'Mar' [10, 20, 30, 40][first_three] # [10, 20, 30]
8. Copies
A slice of a list is a new list, so changing it does not change the original. It is a shallow copy: if the list holds other lists, those inner lists are shared.
xs = [0, 1, 2, 3, 4, 5] ys = xs[:] ys[0] = 99 xs # [0, 1, 2, 3, 4, 5] (xs did not change) grid = [[1], [2]] cp = grid[:] cp[0].append(9) grid # [[1, 9], [2]] (the inner list is shared)
NumPy arrays are different. A slice of an array is a view: it shares the data with the original array, so a change through the slice changes the original. Call .copy() when you need a separate array.
import numpy as np arr = np.array([1, 2, 3, 4]) part = arr[1:3] part[0] = 99 arr.tolist() # [1, 99, 3, 4] safe = arr[1:3].copy()
9. Past the end
An index past the end raises an error. A slice past the end stops at the end of the list, and never raises an error.
xs = [0, 1, 2, 3, 4, 5] xs[4:99] # [4, 5] xs[10:20] # [] (no items there: an empty list, no error) [][:1] # [] (no error on an empty list; [][0] would raise IndexError)
10. Useful patterns
k = 2 xs[k:] + xs[:k] # [2, 3, 4, 5, 0, 1] (moves the first k items to the end) xs[-k:] + xs[:-k] # [4, 5, 0, 1, 2, 3] (moves the last k items to the front) list(zip(xs, xs[1:]))[:3] # [(0, 1), (1, 2), (2, 3)] (pairs each item with the next item)
setup added so this can run · re-runs 4 earlier blocks on this page first
xs = ["alpha", "beta", "gamma"] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included)\nxs[:3] # [0, 1, 2] (the first 3 items)\nxs[3:] # [3, 4, 5] (from index 3 to the end)\nxs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list)\nxs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:3] = [7, 8, 9] # [0, 7, 8, 9, 3, 4, 5] (replaces 2 items with 3 items; the list grows)\nxs[1:4] = [] # [0, 3, 4, 5] (assigning an empty list removes the items at index 1, 2 and 3)\nxs[:0] = [9] # [9, 0, 3, 4, 5] (assigning to an empty slice inserts items)\ndel xs[::2] # [0, 4] (del removes every second item)', 'xs = [0, 1, 2, 3, 4, 5]\nys = xs[:]\nys[0] = 99\nxs # [0, 1, 2, 3, 4, 5] (xs did not change)\ngrid = [[1], [2]]\ncp = grid[:]\ncp[0].append(9)\ngrid # [[1, 9], [2]] (the inner list is shared)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[4:99] # [4, 5]\nxs[10:20] # [] (no items there: an empty list, no error)\n[][:1] # [] (no error on an empty list; [][0] would raise IndexError)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Common mistakes
Changing a string through an index or a slice. s[0] = "P" raises TypeError: 'str' object does not support item assignment (full error page). Build a new string: "P" + s[1:].
Start after stop. With a positive step, a slice whose start comes after its stop is empty. Add a step of -1:
xs[4:1] # [] xs[4:1:-1] # [4, 3, 2]
setup added so this can run · re-runs 4 earlier blocks on this page first
xs = ["alpha", "beta", "gamma"] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included)\nxs[:3] # [0, 1, 2] (the first 3 items)\nxs[3:] # [3, 4, 5] (from index 3 to the end)\nxs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list)\nxs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:3] = [7, 8, 9] # [0, 7, 8, 9, 3, 4, 5] (replaces 2 items with 3 items; the list grows)\nxs[1:4] = [] # [0, 3, 4, 5] (assigning an empty list removes the items at index 1, 2 and 3)\nxs[:0] = [9] # [9, 0, 3, 4, 5] (assigning to an empty slice inserts items)\ndel xs[::2] # [0, 4] (del removes every second item)', 'xs = [0, 1, 2, 3, 4, 5]\nys = xs[:]\nys[0] = 99\nxs # [0, 1, 2, 3, 4, 5] (xs did not change)\ngrid = [[1], [2]]\ncp = grid[:]\ncp[0].append(9)\ngrid # [[1, 9], [2]] (the inner list is shared)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[4:99] # [4, 5]\nxs[10:20] # [] (no items there: an empty list, no error)\n[][:1] # [] (no error on an empty list; [][0] would raise IndexError)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Mixing up a slice and an index. xs[-1:] gives a list with one item, but xs[-1] gives the item itself:
xs[-1:] # [5] xs[-1] # 5
setup added so this can run · re-runs 4 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) xs = _AutoMock('xs') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included)\nxs[:3] # [0, 1, 2] (the first 3 items)\nxs[3:] # [3, 4, 5] (from index 3 to the end)\nxs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list)\nxs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:3] = [7, 8, 9] # [0, 7, 8, 9, 3, 4, 5] (replaces 2 items with 3 items; the list grows)\nxs[1:4] = [] # [0, 3, 4, 5] (assigning an empty list removes the items at index 1, 2 and 3)\nxs[:0] = [9] # [9, 0, 3, 4, 5] (assigning to an empty slice inserts items)\ndel xs[::2] # [0, 4] (del removes every second item)', 'xs = [0, 1, 2, 3, 4, 5]\nys = xs[:]\nys[0] = 99\nxs # [0, 1, 2, 3, 4, 5] (xs did not change)\ngrid = [[1], [2]]\ncp = grid[:]\ncp[0].append(9)\ngrid # [[1, 9], [2]] (the inner list is shared)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[4:99] # [4, 5]\nxs[10:20] # [] (no items there: an empty list, no error)\n[][:1] # [] (no error on an empty list; [][0] would raise IndexError)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
xs[:-k] when k is 0. -0 is the same as 0, so xs[:-0] is an empty list, not the whole list. Write xs[:len(xs) - k] when k can be 0:
k = 0 xs[:-k] # [] xs[:len(xs) - k] # [0, 1, 2, 3, 4, 5]
setup added so this can run · re-runs 4 earlier blocks on this page first
xs = ["alpha", "beta", "gamma"] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:4] # [1, 2, 3] (index 1, 2 and 3; index 4 is not included)\nxs[:3] # [0, 1, 2] (the first 3 items)\nxs[3:] # [3, 4, 5] (from index 3 to the end)\nxs[:] # [0, 1, 2, 3, 4, 5] (a copy of the whole list)\nxs[:2] + xs[2:] # [0, 1, 2, 3, 4, 5] (the two parts join back into the whole list)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[1:3] = [7, 8, 9] # [0, 7, 8, 9, 3, 4, 5] (replaces 2 items with 3 items; the list grows)\nxs[1:4] = [] # [0, 3, 4, 5] (assigning an empty list removes the items at index 1, 2 and 3)\nxs[:0] = [9] # [9, 0, 3, 4, 5] (assigning to an empty slice inserts items)\ndel xs[::2] # [0, 4] (del removes every second item)', 'xs = [0, 1, 2, 3, 4, 5]\nys = xs[:]\nys[0] = 99\nxs # [0, 1, 2, 3, 4, 5] (xs did not change)\ngrid = [[1], [2]]\ncp = grid[:]\ncp[0].append(9)\ngrid # [[1, 9], [2]] (the inner list is shared)', 'xs = [0, 1, 2, 3, 4, 5]\nxs[4:99] # [4, 5]\nxs[10:20] # [] (no items there: an empty list, no error)\n[][:1] # [] (no error on an empty list; [][0] would raise IndexError)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Assigning the wrong number of items to a slice with a step. With a step, the slice and the new items must have the same length. xs[::2] = [0] raises ValueError: attempt to assign sequence of size 1 to extended slice of size 3.
Slicing dict keys. d.keys()[:1] raises TypeError: 'dict_keys' object is not subscriptable. Write list(d)[:1].
See also
- Lists and the Lists cheat sheet
- Strings and the Strings cheat sheet
- Itertools cheat sheet for slicing generators
Practice this
on practicepython.inShort exercises that run in your browser and tell you what your code did, as well as whether a test passed.
Found this useful? Share it with a friend who’s learning Python.