String indexing & slicing
Reach into text by position, take slices, and measure length.
Why reaching into text by position matters
Almost every parsing task starts with position. You pull a fixed-width field out of a log line, strip a known bracket or prefix off an ID, grab the last four characters of an order number, or read a file extension off the end of a name. Before you reach for fancy string methods or regular expressions, indexing and slicing are the cheapest, most predictable way to get at part of a string. Get these solid and half of "clean up this messy text" work becomes trivial.
A string is an indexed sequence
A Python string is an ordered sequence of characters. Every character has a position, called an index, counted from 0 at the front. You can also count from the back with negative indices, where -1 is the last character:
| Counting | p | y | t | h | o | n |
|---|---|---|---|---|---|---|
| From the front | 0 | 1 | 2 | 3 | 4 | 5 |
| From the back | -6 | -5 | -4 | -3 | -2 | -1 |
Reach in with square brackets, and use len() to count characters:
word = "python"
word[0] # "p" first character
word[-1] # "n" last character
len(word) # 6 number of characters
To build the first-and-last string you will need in the Apply, you combine two indexed characters with +: word[0] + word[-1] gives "pn". For a one-character string like "a", both word[0] and word[-1] point at the same character, so you get "aa".
Slicing: half-open ranges
text[start:stop] returns a slice, a new string running from start up to but not including stop:
word[0:3] # "pyt" indices 0, 1, 2
word[2:] # "thon" from 2 to the end
word[:2] # "py" start up to index 2
word[1:-1] # "ytho" drop the first and last character
That word[1:-1] pattern is exactly what the Practice needs. text[1:-1] starts at the second character and stops just before the last, so "[hi]" becomes "hi".
Slicing with a step
A slice takes an optional third number, the step: text[start:stop:step]. The step sets how far to jump between characters, so word[::2] keeps every second character ("pto" from "python"). Leaving start and stop empty runs across the whole string.
A negative step walks backward, so the idiom text[::-1] reverses a string by stepping from the end to the start:
word[::2] # "pto" every second character
word[::-1] # "nohtyp" the whole string, reversed
"abc"[::-1] # "cba"
Reversing with [::-1] is the idiomatic way to test a palindrome: text == text[::-1].
Strings are immutable
You cannot change a character in place. word[0] = "P" raises TypeError. Every operation that "modifies" a string actually builds a new string and leaves the original untouched. That is why slicing returns a fresh value instead of editing word.
Pitfalls
- Indexing out of range raises, slicing does not.
"hi"[5]raisesIndexError, but"hi"[0:5]quietly clamps and returns"hi". Slicing never errors on out-of-range bounds; single-character indexing does. - The empty string has no characters.
""[0]raisesIndexError, sofirst_and_last("")would blow up. Both exercises assume at least one character, but in real code you check for empty input first. - A short slice can go empty, not error.
"ab"[1:-1]is"", becausestart(1) is not beforestop(-1, meaning index 1). No exception, just an empty result.
Interview nuance: Python slicing uses a half-open interval [start, stop). This is not a quirk; it makes the boundary math clean. With non-negative, in-range bounds where start is at or before stop, the slice length is exactly stop - start, and for any index i, s[:i] + s[i:] == s reconstructs the original with no overlap and no gap. Interviewers lean on this half-open convention to check whether you reason about boundaries correctly, the same off-by-one discipline that shows up in array windows and pagination.
word = "python"
print(word[0]) # p
print(word[-1]) # n
print(word[1:-1]) # ytho
print(len(word)) # 6Apply
Your turn
The task this lesson builds to.
Implement first_and_last(text): return a 2-character string made of the first and
last characters of text.
For "python" return "pn". (A one-character string like "a" returns "aa".)
3 hints and 4 automated checks are waiting in the workspace.
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Implement without_ends(text): return text with its first and last characters removed.
For "python" return "ytho". For "[hi]" return "hi".
2 hints and 4 automated checks are waiting in the workspace.