How Python strings really behave: immutability, indexing and slicing, looping over characters, escape sequences and raw strings.
About 35 minBeginner4 exercisesFree
You will be able to
Explain why strings are immutable and build a changed copy instead of editing one in place
Index and slice strings with positive and negative positions and a step
Loop over the characters of a string and test membership with `in`
Write special characters with escape sequences and use raw strings for backslash-heavy text
Most data that reaches a program arrives as text: a customer typing their name into a form, an order reference printed on an invoice, a line in a server log, a message from a support chat. Before you can clean, check or analyse any of it, you need a precise picture of what a string is and what you can and cannot do with one.
You have already met strings, len() and a few string methods. This lesson goes one level deeper: how Python stores a string as a sequence of characters, why you can never change a string in place, how to walk through it character by character, and how to write characters that are hard to type, such as a tab, a newline or a backslash.
A string is a sequence of characters
Every character in a string has a position called an index. The first character is at index 0. Negative indexes count from the end: -1 is the last character, -2 the one before it.
A slicetext[start:stop] gives you a new string from start up to, but not including, stop. Leave out start to begin at the start; leave out stop to run to the end. A third number, the step, takes every n-th character, and a step of -1 walks backwards.
Indexing and slicing an invoice reference
ref ="INV-2025-00417"print(ref[0])# first characterprint(ref[-1])# last characterprint(ref[:3])# the prefixprint(ref[4:8])# the yearprint(ref[-5:])# the last five charactersprint(ref[::2])# every second characterprint(ref[::-1])# reversedprint(len(ref))
Try it: Change the slice for the year to ref[4:9] and notice that it now includes the second dash. Then try ref[50] and ref[50:] to compare an index and a slice that go past the end.
Two details are worth remembering:
An index that is out of range raises IndexError, but a slice that is out of range never does. ref[50:] is simply "", the empty string.
ref[-5:] reads as "the last five characters". It is the cleanest way to take a suffix of any length.
Strings are immutable
Immutable means "cannot be changed after it is created". Every string method and every slice gives you a new string; the original stays exactly as it was. If you try to assign to a position, Python refuses:
You cannot replace a character in place
code ="sku-1042"
code[0]="S"
Try it: Read the error message: 'str' object does not support item assignment. Now replace the second line with code = "S" + code[1:] and print(code).
To "change" a string you build a new one and, if you want, store it back under the same name. The old string is discarded.
A method such as upper() follows the same rule. Calling name.upper() on its own line does nothing useful: it creates an upper-case copy and throws it away. You must keep the result.
Building a new string and keeping the result
code ="sku-1042"
fixed = code[:3].upper()+ code[3:]print(code)# unchangedprint(fixed)
name ="noor"
name.upper()# result discardedprint(name)
name = name.upper()# result keptprint(name)
Try it: Add a line that builds the string "SKU_1042" from fixed by joining two slices around an underscore.
Looping over characters and testing with in
A for loop over a string gives you one character at a time, from left to right. Combined with the in operator you can classify each one. in works two ways on strings:
ch in "aeiou" checks whether one character is one of the listed characters.
"refund" in message checks whether a whole substring appears anywhere inside a longer string.
Counting digits and checking for a word
message ="Order 5521 arrived damaged, please send a refund for 2 items"
digits =0for ch in message:
if ch in"0123456789":
digits +=1print("Digits:", digits)print("refund"in message)print("Refund"in message)# in is case-sensitiveprint(""in message)# the empty string is in every string
Try it: Count the spaces in the message as well, using a second counter inside the same loop.
Some characters cannot be typed directly inside quotes. An escape sequence is a backslash followed by a code that stands for one character:
You write
You get
\n
a newline (line break)
\t
a tab
\\
one backslash
\" and \'
a quote that does not end the string
\u00e9
the character with that Unicode number (here é)
Each escape counts as one character: len("a\tb") is 3.
Escapes in a short report
report ="Item\tQty\nPens\t12\nPaper\t3"print(report)print(len("a\tb"))print("She said \"thanks\" and left")print('It\'s ready')print("Caf\u00e9 order")
Try it: Add a third row for Staplers with a quantity of 7 to the report string, using \t and \n.
Raw strings
Backslashes become a problem when the text itself is full of them, such as Windows folder paths or (later in this track) regular expressions. In "C:\new\tables" Python sees \n and \t and quietly turns them into a newline and a tab.
A raw string has an r before the opening quote: r"C:\new\tables". Inside it, a backslash is an ordinary character and nothing is escaped. One restriction: a raw string cannot end with a single backslash, because \" still stops the quote from closing the string.
A path written two ways
normal ="C:\new\tables"
raw =r"C:\new\tables"print(normal)print(raw)print(len(normal),len(raw))print(raw =="C:\\new\\tables")
Try it: Print repr(normal) and repr(raw). repr() shows the escapes Python is holding, which is a quick way to spot an accidental newline.
Guided practice · ●difficulty 1 of 5 · about 4 min
Count the vowels
A naming tool for a toy shop scores product names by how many vowels they contain. Write a function count_vowels(text) that returns how many characters in text are vowels: a, e, i, o or u, in either upper or lower case.
Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.
Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type
Output
Run your code to see its output, or Check to test it against the requirements.
The solution becomes available after you pass, or after all three hints and two checks.
Challenge · ●●difficulty 2 of 5 · about 6 min
Mask a reference number
When a support agent reads out a customer's booking reference on a recorded call, only the last four characters may be shown. Write mask_reference(ref) that returns a new string where every character except the last four is replaced by *. References of four characters or fewer are returned unchanged.
Spaces and dashes in the hidden part are masked too.
Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.
Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type
Output
Run your code to see its output, or Check to test it against the requirements.
The solution becomes available after you pass, or after all three hints and two checks.
Debugging · ●●difficulty 2 of 5 · about 5 min
The broken report path
An export script builds the full Windows path of a report file. The finance team says the files never appear in C:\shared\new_orders\totals, and when the path is printed it is split across two lines with a strange gap in the middle.
Fix report_path(filename) so that it returns the folder path, a single backslash and then the file name:
Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.
Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type
Output
Run your code to see its output, or Check to test it against the requirements.
The solution becomes available after you pass, or after all three hints and two checks.
Debugging · ●●difficulty 2 of 5 · about 5 min
Capitalise without crashing
A customer-records tool should make the first letter of a company name upper case and leave every other character exactly as typed. Right now it crashes with TypeError: 'str' object does not support item assignment.
Fix capitalise_first(name) so that:
capitalise_first("riyadh branch")# "Riyadh branch"
capitalise_first("al-Rashid Trading")# "Al-Rashid Trading" (the R stays upper case)
capitalise_first("")# ""
Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.
Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type
Output
Run your code to see its output, or Check to test it against the requirements.
The solution becomes available after you pass, or after all three hints and two checks.
Summary
A string is an immutable sequence of characters: methods and slices return new strings, and you keep a result by assigning it.
Indexes start at 0 and negative indexes count from the end; slices exclude the stop position and never raise IndexError.
A for loop visits each character; in tests a single character or a whole substring, case-sensitively.
Escape sequences such as \n, \t and \\ each stand for one character; raw strings (r"...") keep backslashes literally.