Python slicing lets you extract, copy, reverse, and sample parts of a sequence with compact syntax. It works with lists, strings, tuples, ranges, and many third-party sequence types. Once you understand the three positions—start, stop, and step—you can replace many manual loops with expressions that are both readable and efficient.
This Python slicing guide starts with the basics and progresses to negative indexes, copies, slice assignment, multidimensional data, and common mistakes. Review Python lists first if indexing is still new.
The slice syntax
A slice uses sequence[start:stop:step]. The start index is included, the stop index is excluded, and the default step is one. Any component may be omitted.
letters = ["a", "b", "c", "d", "e", "f"]
print(letters[1:4]) # ['b', 'c', 'd']
print(letters[:3]) # ['a', 'b', 'c']
print(letters[3:]) # ['d', 'e', 'f']
print(letters[:]) # a shallow copyThe stop-exclusive rule matches range() and makes lengths predictable: when step is one, items[a:b] normally contains b - a elements. The guide to range in Python explores the same boundary convention.
Slice strings and tuples
Strings and tuples are immutable, so slicing returns new objects without changing the original. The syntax is identical.
language = "Python"
print(language[0:3]) # Pyt
print(language[-3:]) # hon
point = (10, 20, 30, 40)
print(point[1:3]) # (20, 30)A single index returns one element, while a slice returns a sequence. For example, language[0] is the string "P", and language[0:1] is also a string but was produced through slicing.
Negative indexes
Negative indexes count from the end. Index -1 is the last element, -2 is the second-to-last, and so on. They are particularly useful when the sequence length is unknown.
values = [5, 10, 15, 20, 25]
print(values[-1]) # 25
print(values[-3:]) # [15, 20, 25]
print(values[:-1]) # everything except the last itemPython tolerates slice boundaries beyond the sequence length. A single out-of-range index raises IndexError, but an oversized slice safely returns the available elements.
values = [1, 2, 3]
print(values[:100]) # [1, 2, 3]
# print(values[100]) # would raise IndexErrorUse the step value
The optional step controls how indexes advance. A step of two selects every second item; a negative step moves backward.
numbers = list(range(10))
print(numbers[::2]) # even positions
print(numbers[1::2]) # odd positions
print(numbers[::-1]) # reversed copyA zero step is invalid because Python could not advance through the sequence. It raises ValueError. For large data pipelines, remember that list and string slices create new objects; an iterator from itertools.islice can avoid copying.
See the introduction to Python itertools for lazy iteration patterns.
Reverse a sequence
The familiar [::-1] pattern creates a reversed copy. It is concise for strings and small sequences.
word = "level"
is_palindrome = word == word[::-1]
print(is_palindrome)When you only need to iterate backward, reversed(sequence) avoids creating an entire copied list. Choose the expression that communicates whether you need a new sequence or only reverse traversal.
Copy a list with slicing
A full slice creates a shallow list copy. The outer list is new, but nested mutable objects are shared.
original = [[1, 2], [3, 4]]
copied = original[:]
copied.append([5, 6])
print(original) # outer list unchanged
copied[0].append(99)
print(original) # nested list changed tooUse copy.deepcopy() when nested mutable objects also need independent copies. Shallow and deep copying are discussed in the complete Python lists guide.
Modify lists with slice assignment
Lists support assignment to a slice. You can replace, insert, or delete several elements at once. This mutates the original list.
colors = ["red", "green", "blue", "black"]
colors[1:3] = ["yellow", "white"]
print(colors)
colors[2:2] = ["purple"]
print(colors)
colors[-1:] = []
print(colors)The replacement length does not need to match the selected length when the step is one. For an extended slice such as items[::2], however, the replacement must contain exactly the same number of items.
values = [0, 1, 2, 3, 4, 5]
values[::2] = [10, 20, 30]
print(values)Delete with del and a slice
The del statement removes the selected positions without constructing a replacement list.
queue = ["a", "b", "c", "d", "e"]
del queue[1:4]
print(queue) # ['a', 'e']Deletion is an in-place operation. If you need to preserve the input, build a new list from the slices you want to keep.
Name complex slices
The built-in slice() object represents the same start, stop, and step values. Naming it can make repeated or domain-specific selections easier to understand.
record = "2026-07-10|PAID|00042"
date_part = slice(0, 10)
status_part = slice(11, 15)
id_part = slice(16, None)
print(record[date_part])
print(record[status_part])
print(record[id_part])For robust data exchange, fixed-width strings are often less flexible than structured formats such as CSV or JSON. Still, named slices are useful for legacy formats and formatted identifiers.
Chunk a list
Slicing is a simple way to divide a list into batches. This pattern is useful for API requests, database inserts, and paginated processing.
def chunks(items: list[int], size: int):
if size <= 0:
raise ValueError("size must be positive")
for start in range(0, len(items), size):
yield items[start:start + size]
for batch in chunks(list(range(10)), 3):
print(batch)The function uses yield so batches are produced one at a time. Learn more in the guide to efficient Python generators.
How slice boundaries are normalized
Python adjusts omitted and oversized boundaries according to the sequence length and step direction. You can inspect the normalized values with slice.indices(), which is helpful when implementing your own sequence class.
selection = slice(None, None, -2)
start, stop, step = selection.indices(7)
print(start, stop, step)Common mistakes
- Forgetting that the stop index is excluded.
- Using a negative step with boundaries designed for forward movement.
- Assuming
items[:]deeply copies nested objects. - Creating large copies when only iteration is required.
- Confusing a single index result with a one-element slice.
- Using slice assignment without realizing it mutates every reference to the list.
Frequently asked questions
Why does items[2:2] return an empty sequence?
The start and stop positions are equal, so there are no indexes in the half-open interval. During slice assignment, that empty interval becomes a useful insertion point.
What does items[-3:-1] select?
It begins three positions from the end and stops before the final position, usually selecting the third-to-last and second-to-last elements.
Is slicing always fast?
Finding boundaries is fast, but built-in list, tuple, and string slicing copies the selected elements. The cost therefore grows with the slice size. For lazy processing, use iterators or itertools.islice.
Final thoughts
Python slicing is more than shorthand for extracting a list segment. It supports negative positions, sampling, reversal, shallow copies, in-place replacement, deletion, and batching. Keep the start-inclusive and stop-exclusive rule in mind, use named slices when expressions become cryptic, and avoid unnecessary copies in large-data workflows.






