How to slice a list, string, tuple in Python

Modified: | Tags: Python, List, String

In Python, by using a slice (e.g., [2:5:2]), you can extract subsequences from sequence objects, such as lists, strings, tuples, etc.

Basic usage of slices

[start:stop]

In a slice, the start and stop positions for the subsequence are denoted as [start:stop]. The extracted range is start <= x < stop, including the item at start but excluding the item at stop.

l = [0, 10, 20, 30, 40, 50, 60]

print(l[2:5])
# [20, 30, 40]

Think of the positions (indices) for the slice as pointing between elements.

One way to remember how slices work is to think of the indices as pointing between characters, with the left edge of the first character numbered 0. 3. An Informal Introduction to Python - Strings — Python 3.11.3 documentation

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

This example demonstrates slices for strings, but the same concept applies to lists, tuples, and other sequence objects. The case of negative values is described later.

Omitting start extracts the subsequence from the beginning, while omitting stop extracts it to the end. If both are omitted, all items are extracted.

print(l[:3])
# [0, 10, 20]

print(l[3:])
# [30, 40, 50, 60]

print(l[:])
# [0, 10, 20, 30, 40, 50, 60]

Out of range

No error occurs if you specify a position that exceeds the number of items.

print(l[2:10])
# [20, 30, 40, 50, 60]

If no item is selected

No error occurs if you specify start and stop that select no item. An empty list is returned.

print(l[5:2])
# []

print(l[2:2])
# []

print(l[10:20])
# []

[start:stop:step]

You can specify step in addition to start and stop as [start:stop:step]. For example, if step is set to 2, you can select items at odd-numbered or even-numbered positions.

l = [0, 10, 20, 30, 40, 50, 60]

print(l[::2])
# [0, 20, 40, 60]

print(l[1::2])
# [10, 30, 50]

Other examples:

print(l[::3])
# [0, 30, 60]

print(l[2:5:2])
# [20, 40]

As in the previous examples, if step is omitted, it is set to 1.

Extract from the end with a negative value

Negative values for start and stop

Negative values for start and stop represent positions from the end.

The concept of the positions (indices) for the slice is restated below.

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1
l = [0, 10, 20, 30, 40, 50, 60]

print(l[3:-1])
# [30, 40, 50]

print(l[-2:])
# [50, 60]

print(l[-5:-2])
# [20, 30, 40]

Negative values for step

If step is a negative value, the items are selected in reverse order.

Items are selected from the position at start. Note that if start does not indicate a position after stop, the result will be empty.

l = [0, 10, 20, 30, 40, 50, 60]

print(l[5:2:-1])
# [50, 40, 30]

print(l[2:5:-1])
# []

Other examples:

print(l[-2:-5:-1])
# [50, 40, 30]

print(l[-2:2:-1])
# [50, 40, 30]

print(l[5:2:-2])
# [50, 30]

When step is a negative value, omitting start selects from the end, and omitting stop selects to the beginning. If both are omitted, all items are selected.

By omitting start and stop and setting step to -1, you can get a reversed object.

print(l[:3:-1])
# [60, 50, 40]

print(l[3::-1])
# [30, 20, 10, 0]

print(l[::-1])
# [60, 50, 40, 30, 20, 10, 0]

You can also use reverse() and reversed() to reverse lists or strings, tuples, etc. See the following article for details.

Create slice objects with slice()

You can create a slice object using the built-in slice() function. To repeatedly select the items at the same position, you can create the slice object once and reuse it.

slice(start, stop, step) is equivalent to start:stop:step.

l = [0, 10, 20, 30, 40, 50, 60]

sl = slice(2, 5, 2)
print(sl)
# slice(2, 5, 2)

print(type(sl))
# <class 'slice'>

print(l[sl])
# [20, 40]

If two arguments are specified, step is set to None. This is equivalent to start:stop.

sl = slice(2, 5)
print(sl)
# slice(2, 5, None)

print(l[sl])
# [20, 30, 40]

If only one argument is specified, start and step are set to None. This is equivalent to :stop.

sl = slice(2)
print(sl)
# slice(None, 2, None)

print(l[sl])
# [0, 10]

If all arguments are omitted, a TypeError is raised. If you want to create a slice object equivalent to :, explicitly specify None.

# sl = slice()
# TypeError: slice expected at least 1 arguments, got 0

sl = slice(None)
print(sl)
# slice(None, None, None)

print(l[sl])
# [0, 10, 20, 30, 40, 50, 60]

Assign values using slices

You can assign new values to the range selected by slices.

It does not matter if the number of items in the range selected by slicing does not match the number of items (= the length of object) to be assigned.

l = [0, 10, 20, 30, 40, 50, 60]

l[2:5] = [200, 300, 400]
print(l)
# [0, 10, 200, 300, 400, 50, 60]

l[2:5] = [-2, -3]
print(l)
# [0, 10, -2, -3, 50, 60]

l[2:4] = [2000, 3000, 4000, 5000]
print(l)
# [0, 10, 2000, 3000, 4000, 5000, 50, 60]

l[2:6] = [20000]
print(l)
# [0, 10, 20000, 50, 60]

Note that specifying a scalar value on the right side will result in a TypeError.

# l[2:3] = 200
# TypeError: can only assign an iterable

l[2:3] = [200]
print(l)
# [0, 10, 200, 50, 60]

If the right side is empty, the elements in the range selected by the slice will be deleted.

l[1:4] = []
print(l)
# [0, 60]

You can also assign an out-of-range or empty range. The value on the right side is inserted at the specified position.

l = [0, 10, 20, 30, 40, 50, 60]

l[100:200] = [-1, -2, -3]
print(l)
# [0, 10, 20, 30, 40, 50, 60, -1, -2, -3]

l[2:2] = [-100]
print(l)
# [0, 10, -100, 20, 30, 40, 50, 60, -1, -2, -3]

If the number of elements is not equal for the range where step is specified, a ValueError is raised.

l = [0, 10, 20, 30, 40, 50, 60]

l[1::2] = [100, 200, 300]
print(l)
# [0, 100, 20, 200, 40, 300, 60]

# l[1::2] = [100, 200]
# ValueError: attempt to assign sequence of size 2 to extended slice of size 3

To add an element in the middle or at the end of the list, you can also use the insert() and append() methods. See the following article for more information:

Apply slices to a list of lists

When applying a slice to a list of lists (= 2D list), the inner lists are selected.

l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

print(l_2d[1:3])
# [[3, 4, 5], [6, 7, 8]]

To apply a slice to the inner lists, use list comprehensions.

print([l[:2] for l in l_2d[1:3]])
# [[3, 4], [6, 7]]

If you want to get a column, you can transpose it.

l_2d_t = [list(x) for x in zip(*l_2d)]
print(l_2d_t)
# [[0, 3, 6, 9], [1, 4, 7, 10], [2, 5, 8, 11]]

print(l_2d_t[1])
# [1, 4, 7, 10]

Note that if you can use NumPy, it is easier to use NumPy to manipulate multi-dimensional arrays. In NumPy, you can specify a slice for each dimension like [1:4, 2:5].

Slices make shallow copy

Slicing creates a shallow copy. For more information on shallow and deep copying, see the following article.

For example, when a list consists of immutable objects like numbers, assigning the slicing result to a variable and updating its elements won't affect the original object.

l = [0, 10, 20, 30, 40, 50, 60]

l_slice = l[2:5]
print(l_slice)
# [20, 30, 40]

l_slice[1] = 300
print(l_slice)
# [20, 300, 40]

print(l)
# [0, 10, 20, 30, 40, 50, 60]

When a list contains mutable objects like other lists or dictionaries, updating elements in the copied list will also affect the original list.

l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

l_2d_slice = l_2d[1:3]
print(l_2d_slice)
# [[3, 4, 5], [6, 7, 8]]

l_2d_slice[0][1] = 400
print(l_2d_slice)
# [[3, 400, 5], [6, 7, 8]]

print(l_2d)
# [[0, 1, 2], [3, 400, 5], [6, 7, 8], [9, 10, 11]]

In the above example, updating the list within the slice also updates the list in the original object. Likewise, updating the list in the original object also affects the list within the slice.

To avoid this, import the copy module of the standard library and use deepcopy().

import copy

l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

l_2d_slice_deepcopy = copy.deepcopy(l_2d[1:3])
print(l_2d_slice_deepcopy)
# [[3, 4, 5], [6, 7, 8]]

l_2d_slice_deepcopy[0][1] = 400
print(l_2d_slice_deepcopy)
# [[3, 400, 5], [6, 7, 8]]

print(l_2d)
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

Slices for strings and tuples

Although we have focused on examples using lists so far, slices can also be applied to other sequence objects, such as strings (str) and tuples.

However, since str and tuple are immutable, you cannot assign new values to them.

s = 'abcdefg'

print(s[2:5])
# cde

print(s[::-1])
# gfedcba

# s[2:5] = 'CDE'
# TypeError: 'str' object does not support item assignment

t = (0, 10, 20, 30, 40, 50, 60)

print(t[2:5])
# (20, 30, 40)

# t[2:5] = (200, 300, 400)
# TypeError: 'tuple' object does not support item assignment

See the following article for splitting and replacing strings.

Related Categories

Related Articles