Question

How Python Slice Notation Works: A Beginner-Friendly Guide

pythonslicesequence

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 begin
  • stop: where to stop, but not including that position
  • step: 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.

  • start tells you which box to begin at
  • stop tells you the first box you are not allowed to take
  • step tells you how many boxes to jump each time

So in this slice:

a[1:6:2]

You are saying:

  1. Start at box 1
  2. Take an item
  3. Jump by 2
  4. 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.

Take Quiz

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 index 2 and stops before 5
  • nums[:4] starts at the beginning and stops before 4
  • nums[3:] starts at 3 and continues to the end
  • nums[:] 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:

  • ::2 means take every second item
  • 1::2 means start at index 1, then skip by 2
  • 1:6:2 means start at 1, stop before 6, move by 2

Negative indexes

Python also allows negative indexes:

  • -1 is the last item
  • -2 is 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
  • start should usually be to the right of stop
  • stop is 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 = 1
  • stop = 6
  • step = 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 -> 20
  • 3 -> 40
  • 5 -> 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.

Take Quiz

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 stop as 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 item
  • text[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

PatternMeaningExample result
a[i]one item at index ia[2] -> single value
a[i:j]items from i to before ja[1:4]
a[i:j:k]items from i to before j, jumping by ka[1:6:2]
a[:]copy of the whole sequenceall items
a[::-1]reversed copyall items reversed

Slicing vs range()

FeatureSlicerange()
Purposeselects items from an existing sequencegenerates numbers
Syntax stylea[start:stop:step]range(start, stop, step)
Stop is exclusiveyesyes
Can move backwardyesyes

They follow the same boundary idea, which makes them easier to learn together.

Indexing vs slicing

OperationReturnsExample
Indexinga single itemnums[2]
Slicinga new sequencenums[2:3]

Positive vs negative step

Step typeDirectionExample
Positiveleft to righta[1:5:2]
Negativeright to lefta[5:1:-1]

Rule of thumb:

  • positive step: start is usually less than stop
  • negative step: start is usually greater than stop
Take Quiz

Cheat Sheet

sequence[start:stop:step]

Rules

  • start is included
  • stop is excluded
  • step cannot be 0
  • 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 sequence
  • a[5:1] with positive step gives an empty sequence
  • a[1:5:-1] with negative step gives an empty sequence
  • a[:] 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.

Take Quiz

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

  1. Create a list of at least 7 temperature values.
  2. Print the first 3 temperatures.
  3. Print the last 3 temperatures.
  4. Print every second temperature.
  5. Print the temperatures in reverse order.
Take Quiz

Keep learning

Related questions

View more