PYTHON • COLLECTIONS AND STRINGS

String Indexing & Slicing — Index and slice strings and use negative indexing

Learn to pinpoint any character or extract any substring from a Python string with precision.

Historical Context & Motivation

Working with text is one of the most common tasks in programming. Whether you are building a search engine, filtering usernames, or parsing data from a file, you constantly need to pull out specific characters or sections of a string. Early programming languages made this surprisingly difficult, often requiring complex pointer arithmetic just to grab a single letter. Python changed this by introducing an elegant, readable syntax for indexing and slicing that has since influenced many other languages.

1960s
Early String Handling
Languages like FORTRAN and COBOL handle text as fixed-length arrays of characters. Extracting a substring requires manual memory management and counting bytes.
1972
C and Pointer Arithmetic
The C language treats strings as null-terminated character arrays. Programmers use pointer offsets for indexing — powerful, but error-prone and difficult for beginners.
1987
ABC Language's Clean Syntax
The ABC language, a direct predecessor of Python, introduces a cleaner text-handling syntax. Guido van Rossum, Python's creator, works on the ABC project and is inspired by its readability.
1991
Python 0.9 Released
Python launches with bracket-based indexing and colon-based slicing borrowed and refined from ABC. Negative indexing is included from the start, making it easy to access the end of a string.
2008–Today
Python 3 and Unicode
Python 3 makes all strings Unicode by default, so each index position represents a full character — not just a byte. The slicing syntax remains unchanged, proving its long-term design strength.

The core question this lesson answers is straightforward: How do you efficiently access individual characters and substrings within a Python string? Understanding indexing and slicing gives you the tools to manipulate text data with precision and confidence.

Core Principles & Definitions

Before you start indexing and slicing, you need to understand a few foundational ideas about how Python organizes the characters inside a string. A string in Python is a sequence — an ordered collection of characters where each character occupies a numbered position. These positions are called indices (singular: index). Python's indexing system has a few key traits that set it apart.

1

Zero-Based Indexing

The first character of every string sits at index 0, not 1. This convention comes from how computers measure offsets in memory. The second character is at index 1, and so on.
2

Negative Indexing

Python lets you count backwards from the end of a string. Index -1 is the last character, -2 is the second-to-last, and so on. This eliminates the need to calculate the length of the string first.
3

Immutability

Strings in Python are immutable — you can read any character using an index, but you cannot change it in place. To modify text, you create a new string using slicing and concatenation.
4

Slicing with [start:stop:step]

Slicing extracts a substring by specifying a start index (inclusive), a stop index (exclusive), and an optional step (how many positions to skip). This three-part notation is incredibly powerful.
5

Graceful Bounds Handling

Indexing out of range raises an IndexError, but slicing never does — it simply returns what is available. This makes slicing forgiving and safe for variable-length data.
✦ KEY TAKEAWAY
Think of a string like a row of lockers in a school hallway. Each locker has a number painted on it starting from 0. Indexing is opening one specific locker to see what's inside. Slicing is photocopying the contents of several lockers in a row. Negative indexing is just counting from the far end of the hallway instead of the entrance — the last locker is always −1.

Visual Explanation — Index Map

The diagram below shows the string "PYTHON" laid out character by character. Above each character you see the positive index, and below it you see the corresponding negative index. Notice how the two numbering systems always point to the same character.

Each box represents one character of the string "PYTHON". The numbers above the boxes are the positive indices (starting from 0 on the left), and the pink numbers below are the negative indices (starting from −1 on the right). For example, "PYTHON"[0] and "PYTHON"[-6] both give you 'P'.

This dual numbering system is one of Python's most convenient features. You can always reach the last character with s[-1] no matter how long the string is. There is no need to first call len(s) and then subtract 1 — Python handles that for you behind the scenes.

How Indexing & Slicing Work

Single-Character Indexing

To retrieve one character, place a single integer inside square brackets after the string variable or literal. The general form is:

INDEXING SYNTAX
character = string[index]
If index ≥ 0, it counts from the left (starting at 0). If index < 0, Python internally converts it to len(string) + index so that −1 always refers to the last character.

Slice Notation

Slicing uses up to three parameters separated by colons. Each parameter is optional and has a sensible default.

SLICING SYNTAX
substring = string[start : stop : step]
start — the index where the slice begins (inclusive, default 0). stop — the index where the slice ends (exclusive, default len(string)). step — how many positions to advance each time (default 1). A step of −1 reverses the string.

Negative Index Conversion

NEGATIVE INDEX FORMULA
effective_index = len(string) + negative_index
For example, if string = "PYTHON" (length 6) and negative_index = -2, then effective_index = 6 + (−2) = 4, which corresponds to 'O'.
⚠️ Watch Out for Off-by-One Errors
The most common mistake is forgetting that the stop value is exclusive. Writing s[1:3] gives you indices 1 and 2, but not 3. Think of stop as a fence — the slice goes up to it but never includes it.

Common Slice Patterns & Detailed Breakdown

Once you understand the basic [start:stop:step] notation, you can combine defaults and negative values to produce a variety of useful patterns. The diagram below illustrates several common slices applied to the string "ABCDEFGH".

Seven common slice patterns applied to s = "ABCDEFGH". Note how omitting start defaults to 0, omitting stop defaults to the end, and a step of -1 reverses the string entirely.
Summary of slice parameters and results for s = "ABCDEFGH"
Slice ExpressionstartstopstepResult
s[2:5]251 (default)"CDE"
s[:4]0 (default)41 (default)"ABCD"
s[5:]58 (default)1 (default)"FGH"
s[::2]0 (default)8 (default)2"ACEG"
s[::-1]endbeginning−1"HGFEDCBA"
s[-3:]−3 → 58 (default)1 (default)"FGH"
s[1:-1]1−1 → 71 (default)"BCDEFG"

Worked Example — Extracting a Domain from an Email

Let's solve a realistic problem: given the email address email = "student@school.edu", extract just the domain name "school.edu" using indexing and slicing.

Extracting a Domain from an Email Address
1
Step 1 — Identify the string and goalWe have email = "student@school.edu". The domain starts right after the @ symbol and runs to the end of the string. Our task is to find the position of @ and slice everything that comes after it.
2
Step 2 — Find the index of '@'We use email.find("@") which returns the index of the first occurrence of "@". Let's count: s, t, u, d, e, n, t — that is indices 0 through 6. The @ is at index 7.
at_pos = 7
3
Step 3 — Slice from one past '@' to the endThe domain starts at index at_pos + 1 which is 8. We slice to the end by omitting the stop value:
domain = email[8:]
4
Step 4 — Verify the resultRunning print(domain) outputs the substring starting at index 8 through the end of the string.
Output: school.edu
5
Step 5 — Combine into one clean expressionIn practice, you can combine these into a single line. The expression email[email.find('@') + 1:] finds the @ and slices everything after it in one step. This is a pattern you will see frequently in real-world code.
"school.edu"

Strengths, Limitations & Common Pitfalls

Strengths and pitfalls of Python string indexing and slicing
FeatureStrengthPitfall / Limitation
Zero-based indexingConsistent with lists, tuples, and most programming languagesOff-by-one errors are common for beginners who expect counting from 1
Negative indexingQuick access to end of string without knowing its lengthCan be confusing when mixed with positive indices in slices
Exclusive stopMakes calculating slice length easy: stop − start = lengthBeginners often include one too few characters
Step parameterPowerful for reversing, skipping, or sampling charactersNegative step with start/stop requires careful coordinate thinking
ImmutabilityStrings are safe to pass around — no accidental changesCannot assign to an index; must build a new string instead
✦ KEY TAKEAWAY
The exclusive stop value might seem annoying at first, but it has a beautiful property: the length of any slice s[a:b] is exactly b − a characters, and adjacent slices s[:k] + s[k:] always reconstruct the original string. This is like cutting a ribbon at position k — the two pieces always rejoin perfectly with no overlap and no gap.

Connection to Lists, Tuples & Advanced Topics

One of the most powerful aspects of Python's indexing and slicing syntax is that it works identically across all sequence types. Once you master slicing on strings, you already know how to slice lists, tuples, and even ranges. The table below compares how the same syntax applies to strings versus lists.

Comparing string slicing and list slicing
FeatureStringsLists
Element typeSingle characters (strings of length 1)Any Python object
Mutable?No — cannot assign to an indexYes — can assign and also do slice assignment
x[2:5]Returns a new stringReturns a new list (shallow copy)
x[::-1]Reversed stringReversed list
Negative indexingFully supportedFully supported

As you progress, you will encounter slice objects created with slice(start, stop, step) which let you store a slicing pattern in a variable and reuse it. Libraries like NumPy extend slicing to multiple dimensions for working with matrices and arrays. The foundation you build today transfers directly to those advanced tools.

Practice Problems

Try these five problems to solidify your understanding. They progress from simple recall to creative problem solving. For each, assume s = "COMPUTATION" unless stated otherwise.

PROBLEM 1 — CONCEPTUAL
Explain why s[0] gives the first character of a string, while s[-1] gives the last. Why did Python's designers choose to start indexing at 0 instead of 1?
PROBLEM 2 — BASIC CALCULATION
Given s = "COMPUTATION", what does each of the following evaluate to? (a) s[3] (b) s[-4] (c) s[4:8]
PROBLEM 3 — INTERMEDIATE
Write a Python expression using slicing (no loops or methods) that takes s = "COMPUTATION" and produces the string "CUP". Hint: think about step values and negative indexing.
PROBLEM 4 — APPLIED
A phone number is stored as phone = "(555) 867-5309". Using only indexing and slicing, extract the area code "555" and the last four digits "5309". Show your expressions.
PROBLEM 5 — CRITICAL THINKING
A student writes the function below to check if a string is a palindrome (reads the same forwards and backwards). Explain how the slicing works and identify one limitation of this approach. def is_palindrome(word): return word == word[::-1]

Lesson Summary

Python strings are ordered sequences of characters that support zero-based indexing — the first character is at position 0. You can access individual characters with bracket notation like s[i], and Python's negative indexing lets you count from the end, where s[-1] is always the last character. Slicing uses the syntax s[start:stop:step] where start is inclusive, stop is exclusive, and step controls direction and spacing.

Key patterns to remember: s[:k] gets the first k characters, s[-k:] gets the last k, and s[::-1] produces a reversed copy. Because strings are immutable, every slice creates a new string without altering the original. This same indexing and slicing syntax applies to lists and tuples, making it one of the most transferable skills in Python.

Varsity Tutors • Python • String Indexing & Slicing