Text Is Not a Single Thing
A string looks like one piece of text until you need its third character. Slice a sentence apart by position and find out why the end index is never the one you first guess.
A string looks like one piece of text until you need the third character of it. Underneath, Python treats it as a sequence — the same idea it uses for a list — and that changes what “the third character” actually means.
A string is a sequence, not a word
Index a string with square brackets, the same way you would a list, and counting starts at 0, not 1.
$ >>> name = "python"$ >>> name[0]$ 'p'$ >>> name[5]$ 'n'name[0] is the first character precisely because Python counts positions, not places in line — position 0 is where you start counting from, and it happens to hold the first character.
Slicing without the off-by-one
A slice, text[start:end], pulls out a whole range at once. The part everyone gets wrong at least once: end is not included.
>>> text[4:8]
'with'
The character at index 8is not included — a slice runs up to, but not through, its end index. That is why a slice's length is always end - start, here 4.
Nothing about a string ever changes in place
Try to change one character of a string directly, and Python refuses outright.
$ >>> name = "python"$ >>> name[0] = "P"$ TypeError: 'str' object does not support item assignmentStrings are immutable — once created, a string never changes. Every method that looks like it edits one, such as .upper() or .replace(), actually builds and returns a brand new string, leaving the original exactly as it was.
$ >>> name = "python"$ >>> name.upper()$ 'PYTHON'$ >>> name$ 'python'Key takeaways
- A string is indexed like any sequence, and counting starts at 0.
- A slice text[start:end] never includes the character at end — its length is always end minus start.
- Strings are immutable. Nothing you do to one changes it in place.
- String methods like .upper() return a new string. Reassign the name to keep the result.