Python String Indexing & Slicing: Access and Cut Text

Python indexing gets one character from a string by position, and slicing gets a range of characters. Indexes start at 0, negative indexes count from the end, and slices use the form string[start:stop:step] to build a new string without changing the original. This article shows how the syntax works, how Python counts positions, and the mistakes that cause confusing results.

Difficulty: Beginner

Helpful to know first: basic Python variables, strings as text values, and how zero-based counting works.

How Python String Indexing and Slicing Work

A Python string is an ordered sequence of characters. Indexing means picking one character by its position, and slicing means taking a smaller string made from a range of positions.

Use indexing when you want one character, such as the first letter of a name. Use slicing when you want several characters, such as a prefix, a suffix, or a substring.

Python uses zero-based indexing. That means the first character is at index 0, the second character is at index 1, and so on.

Python
word = "Python"

print(word[0])
print(word[1])
print(word[-1])
# Output:
# P
# y
# n

In that example, word[0] returns the first character, word[1] returns the second, and word[-1] returns the last character. Negative indexes count backward from the end of the string.

Indexing a String One Character at a Time

Indexing is the simplest way to read a single character. If the index is valid, Python returns a one-character string. If it is too large or too small, Python raises an error.

Reading the first, middle, and last characters

When you know the position you want, indexing is direct and clear. This is common when you read file names, check prefixes, or look at fixed-format text.

Python
text = "camera"

print(text[0])
print(text[3])
print(text[-1])
# Output:
# c
# e
# a

The string "camera" has six characters. Index 0 is c, index 3 is e, and index -1 is the last character, a.

Why negative indexes are useful

Negative indexes are helpful when you care about the end of the string more than the beginning. For example, the last letter of a word or the final extension in a file name is often easier to reach with -1 than by counting the full length.

Python
filename = "notes.txt"

print(filename[-1])
print(filename[-4])
# Output:
# t
# .

The last character is t. Four characters from the end is the dot before the extension. Negative indexes still follow the same rule: -1 is the last character, -2 is the character before that, and so on.

Slicing a String to Get a Range of Characters

A slice extracts a part of the string and returns a new string. The basic form is string[start:stop]. The start position is included, but the stop position is not.

That last detail is the part many beginners miss. If you write text[1:4], Python returns positions 1, 2, and 3, but not 4.

The start is included and the stop is excluded

Think of a slice like cutting between positions, not on positions. The left edge belongs to the slice, and the right edge does not.

Python
word = "Python"

print(word[1:4])
print(word[:2])
print(word[4:])
# Output:
# yth
# Py
# on

word[1:4] gives the letters from index 1 through 3. word[:2] means “from the start up to, but not including, index 2.” word[4:] means “from index 4 to the end.”

Getting prefixes, suffixes, and middle parts

Slicing is the standard way to get common pieces of a string without writing loops. Prefixes, suffixes, and middle sections all use the same syntax.

Python
email = "[email protected]"

print(email[:7])
print(email[8:])
print(email[8:15])
# Output:
# student
# example.com
# example

The first slice gets the username prefix. The second slice gets the domain name. The third slice gets only the middle part you asked for. Python does not change the original string in any of these cases.

Using Step Values to Skip Characters or Reverse Text

The full slice form is string[start:stop:step]. The step tells Python how far to move between characters. A step of 2 skips every other character. A step of -1 walks backward through the string.

Skipping every other character

Positive steps are useful when you want regular spacing, such as every second letter or every third character in a code string.

Python
letters = "abcdefg"

print(letters[::2])
print(letters[1::2])
# Output:
# aceg
# bdf

The first slice starts at the beginning and takes every second character. The second slice starts at index 1, then takes every second character from there.

Reversing a string with a negative step

When the step is negative, Python moves from right to left. The most common example is reversing a string with [::-1].

Python
word = "straw"

print(word[::-1])
print(word[-1:-6:-1])
# Output:
# warts
# warts

The first slice reverses the whole string. The second slice does the same thing, but it shows how negative start, stop, and step values can work together.

How Slice Boundaries Behave in Detail

Slice boundaries follow rules that are easy to miss if you only memorize the syntax. Understanding these rules helps you avoid off-by-one errors and makes the results predictable.

Out-of-range slice limits do not cause an error

Unlike indexing, slicing is forgiving. If a slice start or stop is outside the string, Python trims the request to the available characters instead of raising an error.

Python
name = "Ada"

print(name[0:10])
print(name[-10:2])
# Output:
# Ada
# Ad

This is one reason slices are safer than index access when the exact length is not known. Python quietly uses the part that exists.

A slice always creates a new string

Strings in Python are immutable, which means you cannot change a string in place. A slice returns a new string object with the selected characters.

Python
original = "hello"
part = original[1:4]

print(original)
print(part)
# Output:
# hello
# ell

The original string stays the same. The slice is a separate value, even though it was built from the original text.

Indexing and slicing follow the same character positions

If you can count the positions for indexing, you can use the same positions for slicing. For example, if index 3 is the character you want to include, then the slice stop must be 4 to include it.

Python
text = "abcdef"

print(text[2])
print(text[2:3])
# Output:
# c
# c

That relationship is useful because it helps you move between “one character” and “a range of characters” without changing your counting method.

Predict the Output

These short examples test the most common misunderstandings: whether slices include the stop index and what negative steps do.

Predict the result of a slice with a stop index

What does this print?

Python
text = "planet"
print(text[1:4])
Show answer

It prints lan. The slice starts at index 1 and stops before index 4, so it includes l, a, and n.

Terminal
# text = "planet"
# indices: 0 1 2 3 4 5
# letters: p l a n e t
# text[1:4] -> "lan"

Predict the result of a reverse slice

What does this print?

Python
text = "abcde"
print(text[::-1])
Show answer

It prints edcba. A step of -1 tells Python to move backward one character at a time, starting from the end of the string.

Terminal
# text = "abcde"
# text[::-1] -> "edcba"

Common Mistakes With String Indexing and Slicing

Using the last index instead of one past the end

Beginners often expect the stop value in a slice to work like an inclusive endpoint. In Python, the stop value is excluded, so you need one more than the last index you want.

Problem: This slice misses the final character because the stop index is not included.

Python
word = "Python"

print(word[0:5])
# Output: Pytho

Fix: Use one past the last character you want, or leave the stop blank when you want the rest of the string.

Python
word = "Python"

print(word[0:6])
print(word[:])
# Output:
# Python
# Python

The corrected version works because the stop index is now far enough to include the final character.

Using an index that is outside the string

Indexing is stricter than slicing. If you ask for a character that does not exist, Python raises an error instead of trimming the request.

Problem: This code asks for character 4 in a three-character string, so Python cannot return a result.

Python
name = "Ada"

print(name[3])
# IndexError: string index out of range

Fix: Use a valid index, or use slicing if you want Python to handle ranges that may be shorter than expected.

Python
name = "Ada"

print(name[2])
print(name[2:10])
# Output:
# a
# a

The corrected version works because index 2 is valid, and the slice safely stops at the end of the string.

Forgetting that a slice with a negative step needs matching bounds

When the step is negative, the start and stop positions move in the opposite direction from normal slices. If you keep them in the same order, the slice can come back empty.

Problem: This slice looks like it should reverse part of the string, but the boundaries move the wrong way for a negative step.

Python
text = "abcdef"

print(text[1:4:-1])
# Output: 

Fix: Put the larger index first when stepping backward.

Python
text = "abcdef"

print(text[4:1:-1])
# Output:
# edc

The corrected version works because the slice starts later in the string and moves backward toward the stop position.

Find the Bug

One of the slices below does not do what the author wanted. Find the bug and fix it.

Python
code = "python"
first_three = code[0:3]
last_three = code[-3:]

print(first_three, last_three)
# Expected output: pyt hon
Show answer

The bug is in the expected result, not the slice syntax. The code actually prints pyt hon only if there is a space between the values when print joins them. The slices themselves are correct.

The real issue is that the expected output may confuse the two substrings with one continuous word. If the goal is to show the pieces separately, the code is fine. If the goal is to create one string, you need to join them.

Python
code = "python"
first_three = code[0:3]
last_three = code[-3:]

print(first_three + last_three)
# Output: python

The fixed version works because string concatenation combines the two slices into one value.

When to Use Indexing, Slicing, or Something Else

Indexing and slicing are best when you already know the positions you need. If you need to search for a character, split by a separator, or remove whitespace, another string method may be a better fit.

TaskBest choiceWhy
Get one known characterIndexingDirect, simple, and readable
Get a prefix, suffix, or substringSlicingFlexible and concise
Find text by a delimitersplit()Works by content, not position
Trim whitespacestrip()Designed for leading and trailing spaces
Check whether a string starts or ends with somethingstartswith() or endswith()Clearer than manual index checks

Use slicing when the position matters. Use string methods when the content matters more than the position.

Interview Questions About String Indexing and Slicing

Why does Python use zero-based indexing?

Zero-based indexing makes the first position 0, which matches how slices are counted from a starting point. It also fits the way Python and many other languages work with ranges and offsets.

What is the difference between s[0] and s[:1]?

They usually return the same text when the string is not empty, but s[0] returns one character and can raise IndexError if the string is empty. s[:1] always returns a string and gives an empty string for empty input.

Why does slicing not change the original string?

Strings are immutable in Python. A slice builds a new string value, so the original text stays unchanged.

Try It Yourself

Write a program that takes the string "programming" and prints:

  • the first four characters
  • the last four characters
  • the string reversed

Expected output:

Python
prog
ming
gnimmargorp

Hint: use one normal slice, one negative slice, and one reverse slice.

Show solution
Python
text = "programming"

print(text[:4])
print(text[-4:])
print(text[::-1])
# Output:
# prog
# ming
# gnimmargorp

Summary

  • Indexing gets one character with string[index].
  • Slicing gets a range with string[start:stop:step].
  • The start of a slice is included, but the stop is excluded.
  • Negative indexes count from the end of the string.
  • Slices do not raise IndexError when the limits go past the string length.
  • Strings are immutable, so slicing returns a new string instead of changing the original.

Next, learn how Python string methods such as split(), strip(), and replace() work with the same text values.