Question
How does Python's slice notation work? For example, when writing expressions such as a[x:y:z], a[:], or a[::2], how can you determine which elements are included in the result?
Examples to understand include:
a[x:y:z]
a[:]
a[::2]
The main goal is to understand how Python interprets the start, stop, and step parts of a slice, and which items from a sequence end up in the sliced result.
Short Answer
By the end of this page, you will understand how Python slicing selects elements from sequences like lists, strings, and tuples. You will learn what start, stop, and step mean, why the stop index is excluded, how negative indexes and negative steps work, and how to read common slice patterns such as a[:], a[1:4], and a[::-1].
Concept
Python slicing is a way to extract part of a sequence. A sequence can be a list, string, tuple, or other object that supports indexing.
The general form is:
sequence[start:stop:step]
Each part has a job:
start: where to beginstop: where to stop, but not including that positionstep: how far to move each time
You can leave out parts, and Python fills in sensible defaults.
For example:
nums = [10, 20, 30, 40, 50, 60]
print(nums[1:4])
Output:
[20, 30, 40]
This starts at index 1, moves forward by 1, and stops before index 4.
Slicing matters because it is used everywhere in Python:
- getting part of a list
- trimming strings
- copying sequences
- skipping items
- reversing data
- processing batches of data
A key rule is that the stop index is exclusive. That means Python includes items up to, but not including, stop. This makes slices consistent with range() and helps avoid off-by-one confusion when joining adjacent slices.
For example:
letters = ['a', 'b', 'c', 'd', 'e']
print(letters[0:2]) # ['a', 'b']
print(letters[2:5]) # ['c', 'd', 'e']
These slices fit together cleanly without overlap.
If step is negative, Python moves backward through the sequence.
letters = ['a', 'b', 'c', 'd', 'e']
print(letters[::-1])
Output:
['e', 'd', 'c', 'b', 'a']
Mental Model
Think of a slice like walking through numbered boxes on a shelf.
starttells you which box to begin atstoptells you the first box you are not allowed to takesteptells you how many boxes to jump each time
So in this slice:
a[1:6:2]
You are saying:
- Start at box
1 - Take an item
- Jump by
2 - Stop before box
6
If the boxes contain:
['a', 'b', 'c', 'd', 'e', 'f', 'g']
Then you take:
- index
1->'b' - index
3->'d' - index
5->'f'
Result:
['b', 'd', 'f']
For negative steps, imagine walking backward along the shelf instead of forward.
a[::-1]
This means:
- start from the end
- move backward by 1
- continue until the beginning is passed
So it produces a reversed copy.
Syntax and Examples
The full syntax is:
sequence[start:stop:step]
All three parts are optional:
sequence[:] # copy all items
sequence[start:] # from start to the end
sequence[:stop] # from the beginning to before stop
sequence[::step] # whole sequence, skipping by step
Basic examples
nums = [0, 1, 2, 3, 4, 5, 6]
print(nums[2:5]) # [2, 3, 4]
print(nums[:4]) # [0, 1, 2, 3]
print(nums[3:]) # [3, 4, 5, 6]
print(nums[:]) # [0, 1, 2, 3, 4, 5, 6]
Explanation:
nums[2:5]starts at index2and stops before5nums[:4]starts at the beginning and stops before4nums[3:]starts at3and continues to the endnums[:]returns a shallow copy of the whole sequence
Using step
nums = [0, 1, 2, 3, 4, 5, 6]
print(nums[::2]) # [0, 2, 4, 6]
print(nums[1::2]) # [1, 3, 5]
print(nums[1:6:2]) # [1, 3, 5]
Explanation:
::2means take every second item1::2means start at index1, then skip by21:6:2means start at1, stop before6, move by2
Negative indexes
Python also allows negative indexes:
-1is the last item-2is the second-to-last item
letters = ['a', 'b', 'c', 'd', 'e']
print(letters[-4:-1]) # ['b', 'c', 'd']
Negative step
letters = ['a', 'b', 'c', 'd', 'e']
print(letters[::-1]) # ['e', 'd', 'c', 'b', 'a']
print(letters[4:1:-1]) # ['e', 'd', 'c']
When the step is negative:
- slicing moves backward
startshould usually be to the right ofstopstopis still excluded
Step by Step Execution
Consider this example:
nums = [10, 20, 30, 40, 50, 60, 70]
result = nums[1:6:2]
print(result)
Output:
[20, 40, 60]
Now trace it step by step.
Step 1: Understand the indexes
# index: 0 1 2 3 4 5 6
nums = [10, 20, 30, 40, 50, 60, 70]
Step 2: Read the slice parts
nums[1:6:2]
start = 1stop = 6step = 2
Step 3: Begin at index 1
Index 1 contains 20, so that is included.
Step 4: Move by 2 each time
From index 1:
1->203->405->60- next would be
7, but that is past the stop boundary
Step 5: Stop before index 6
Index 6 is not included because stop is exclusive.
So the result is:
[20, 40, 60]
Reverse example
text = "PYTHON"
print(text[5:1:-1])
Indexes:
# index: 0 1 2 3 4 5
# chars: P Y T H O N
Trace:
- start at index
5->N - move backward by
1 - include index
4->O - include index
3->H - include index
2->T - stop before index
1
Result:
'NOHT'
Real World Use Cases
Slicing is useful in many everyday Python tasks.
Working with text
filename = "report.csv"
print(filename[:-4]) # report
This removes the file extension by taking everything except the last 4 characters.
Getting the first few records
users = ["Ana", "Ben", "Cara", "Dan", "Eva"]
print(users[:3])
Useful for previews, dashboards, or showing the top results.
Skipping items in data
values = [5, 10, 15, 20, 25, 30]
print(values[::2])
This can be used to sample every second reading from collected data.
Reversing data
recent = [1, 2, 3, 4, 5]
print(recent[::-1])
Helpful when displaying newest items first.
Splitting data into parts
items = [1, 2, 3, 4, 5, 6]
left = items[:3]
right = items[3:]
This pattern is common when dividing data into chunks.
Trimming logs or messages
message = "ERROR: invalid token"
print(message[:5])
Useful when parsing prefixes, codes, or short identifiers.
Real Codebase Usage
In real projects, slicing often appears as a small but important tool.
Creating safe subsets of data
Developers often slice data before processing it:
batch = records[:100]
This is common in scripts, APIs, and data pipelines.
Copying lists
copied = original[:]
This creates a shallow copy of the list. It is often used when code should avoid modifying the original list.
Limiting output
top_results = search_results[:10]
Very common in web apps, pagination logic, and command-line tools.
Reversing for display
latest_logs = logs[::-1]
Useful when the newest entries should appear first.
Combining slicing with validation
A guard clause may check input length before slicing:
def get_prefix(code):
if len(code) < 3:
return code
return code[:3]
This prevents unexpected assumptions.
Processing ranges without loops everywhere
evens = numbers[::2]
odds = numbers[1::2]
This is a compact alternative to manual indexing loops when the intent is simply to skip regularly.
Important note about copies
For lists, a slice creates a new list, but nested objects inside it are still shared:
data = [[1], [2], [3]]
copy = data[:]
copy[0].append(99)
print(data) # [[1, 99], [2], [3]]
So slicing copies the outer list, not deep nested contents.
Common Mistakes
1. Forgetting that stop is excluded
Broken expectation:
nums = [0, 1, 2, 3, 4]
print(nums[1:3]) # beginner may expect [1, 2, 3]
Actual result:
[1, 2]
How to avoid it:
- Remember: Python stops before
stop - Think of
stopas the first index not included
2. Using the wrong direction with a negative step
Broken code:
nums = [0, 1, 2, 3, 4]
print(nums[1:4:-1])
Result:
[]
Why:
- A negative step moves backward
- But this slice tries to start left and go further right
Correct version:
print(nums[4:1:-1]) # [4, 3, 2]
3. Confusing indexing with slicing
text = "hello"
print(text[1]) # 'e'
print(text[1:2]) # 'e'
These look similar, but they are different:
text[1]gives one itemtext[1:2]gives a slice containing a sequence of length 1
With lists this is easier to notice:
nums = [10, 20, 30]
print(nums[1]) # 20
print(nums[1:2]) # [20]
4. Assuming a[:] is a deep copy
Broken assumption:
a = [[1], [2]]
b = a[:]
b[0].append(99)
print(a)
Output:
[[1, 99], [2]]
How to avoid it:
a[:]copies only the outer container- Use
copy.deepcopy()if you need a deep copy
5. Using a step of zero
Broken code:
nums = [1, 2, 3]
print(nums[::0])
This raises an error because step cannot be zero.
How to avoid it:
- Always use a nonzero step
6. Misreading negative indexes
letters = ['a', 'b', 'c', 'd']
print(letters[-1]) # 'd'
print(letters[-3:-1]) # ['b', 'c']
Negative indexes count from the end, not from the beginning.
Comparisons
Slicing patterns compared
| Pattern | Meaning | Example result |
|---|---|---|
a[i] | one item at index i | a[2] -> single value |
a[i:j] | items from i to before j | a[1:4] |
a[i:j:k] | items from i to before j, jumping by k | a[1:6:2] |
a[:] | copy of the whole sequence | all items |
a[::-1] | reversed copy | all items reversed |
Slicing vs range()
| Feature | Slice | range() |
|---|---|---|
| Purpose | selects items from an existing sequence | generates numbers |
| Syntax style | a[start:stop:step] | range(start, stop, step) |
| Stop is exclusive | yes | yes |
| Can move backward | yes | yes |
They follow the same boundary idea, which makes them easier to learn together.
Indexing vs slicing
| Operation | Returns | Example |
|---|---|---|
| Indexing | a single item | nums[2] |
| Slicing | a new sequence | nums[2:3] |
Positive vs negative step
| Step type | Direction | Example |
|---|---|---|
| Positive | left to right | a[1:5:2] |
| Negative | right to left | a[5:1:-1] |
Rule of thumb:
- positive step:
startis usually less thanstop - negative step:
startis usually greater thanstop
Cheat Sheet
sequence[start:stop:step]
Rules
startis includedstopis excludedstepcannot be0- omitted values use defaults
Common patterns
a[:] # all items
a[2:] # from index 2 to end
a[:5] # from start to before index 5
a[::2] # every second item
a[1::2] # every second item, starting at index 1
a[::-1] # reversed copy
Negative indexes
a[-1] # last item
a[-2] # second-last item
a[:-1] # all except last
a[-3:] # last three items
Direction rules
- positive step moves forward
- negative step moves backward
- if direction and boundaries do not match, result may be empty
Examples
nums = [0, 1, 2, 3, 4, 5]
nums[1:4] # [1, 2, 3]
nums[:3] # [0, 1, 2]
nums[3:] # [3, 4, 5]
nums[::2] # [0, 2, 4]
nums[::-1] # [5, 4, 3, 2, 1, 0]
Important edge cases
a[1:1]gives an empty sequencea[5:1]with positive step gives an empty sequencea[1:5:-1]with negative step gives an empty sequencea[:]is a shallow copy for lists
FAQ
What does a[x:y:z] mean in Python?
It means: start at index x, stop before index y, and move by z each time.
Why is the stop index not included in a slice?
Python uses an exclusive stop so that slices fit together cleanly and match the behavior of range().
What does a[:] do?
It returns a slice of the whole sequence. For lists, this creates a shallow copy.
What does a[::2] mean?
It means take every second item from the entire sequence.
How do negative indexes work in slices?
Negative indexes count from the end. For example, -1 is the last item.
How do I reverse a list or string with slicing?
Use:
a[::-1]
This walks through the sequence backward.
Why does my slice return an empty list?
Usually the step direction does not match the start and stop positions. For example, a negative step moves backward, not forward.
Is slicing available only for lists?
No. Many Python sequence types support slicing, including strings, tuples, and more.
Mini Project
Description
Build a small Python program that demonstrates common slicing patterns on a list of daily temperatures. This project helps you practice reading slices, selecting ranges, skipping items, and reversing data in a realistic context.
Goal
Create a script that extracts useful subsets from a temperature list using Python slicing.
Requirements
- Create a list of at least 7 temperature values.
- Print the first 3 temperatures.
- Print the last 3 temperatures.
- Print every second temperature.
- Print the temperatures in reverse order.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Add Rows to a Pandas DataFrame in Python
Learn how to add rows to a Pandas DataFrame, why repeated row appends are slow, and when to use loc, concat, or record lists.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.