Lesson 3: Strings and String Methods
🎧 Listen to this lesson
Programs are made of text: names, messages, files, web pages, user input. Python's string type — text in quotes — is therefore one of its hardest-working tools. This lesson teaches you to take strings apart (indexing and slicing), ask them questions (membership and length), and reshape them with methods — functions attached to a value, called with a dot. Strings drive real work: cleaning messy user input, parsing lines of data, and building messages, all of which appear constantly in later lessons.
Indexing and slicing: reaching inside a string
Every character in a string has a position, called an index, counting from 0. In s = "python", s[0] is "p" and s[1] is "y". Negative indices count from the end: s[-1] is "n", s[-2] is "o". A slice takes a range, s[start:stop], from start up to but not including stop:
s = "python"
print(s[0]) # first character
print(s[-1]) # last character
print(s[1:4]) # characters at indices 1, 2, 3
print(s[:3]) # from the start up to index 3
print(s[3:]) # from index 3 to the end
print(len(s)) # how many characters?
Output:
p
n
yth
pyt
hon
6
The excluded end is the slice rule that causes every beginner's off-by-one surprise: s[1:4] has three characters, indices 1, 2 and 3. Think of indices as fence posts between characters — a slice from post 1 to post 4 spans three gaps. Strings are immutable: once created they cannot be changed in place. s[0] = "P" raises TypeError: 'str' object does not support item assignment. Methods that appear to change a string actually build and return a new one, so you must store the result: s = s.upper().
Methods: asking strings to reshape themselves
Call a method with a dot: value.method(arguments). The workhorses:
message = " Hello, Python! "
print(message.strip()) # remove surrounding whitespace
print(message.lower()) # all lowercase (spaces stay!)
print(message.strip().lower()) # methods chain: strip, then lower
print("hello".upper()) # all uppercase
print("a,b,c".split(",")) # split into a list at commas
print("-".join(["a", "b"])) # join a list into one string
print("hello world".replace("world", "Python"))
print("ell" in "hello") # membership test
print("banana".count("a")) # how many times does "a" appear?
Output:
Hello, Python!
hello, python!
hello, python!
HELLO
['a', 'b', 'c']
a-b
hello Python
True
3
Notice line 2 kept its spaces: lower() changes letters only, which is why real cleaning chains strip().lower(). And notice split returns a list (square brackets) — the first member of the family Lesson 5 covers properly. Two of these methods run real programs by themselves: split(",") parses a line of CSV data into fields, and normalising with strip().lower() before a comparison stops "Yes", "yes " and "YES" from looking like three different answers.
Building text with f-strings
You met f-strings in Lesson 2: prefix a string with f and embed expressions in braces. They also format numbers — {value:.2f} rounds a float to two decimals inside the text, and {value:>6} pads it to width 6 for tidy columns:
name = "Ada"
score = 92.5
print(f"{name} scored {score}")
print(f"{name} scored {score:.0f} points") # .0f: no decimals
Output: Ada scored 92.5, then Ada scored 92 points. Look closely at that second line: .0f rounded 92.5 down to 92. Python rounds halves to the nearest even number (so 92.5 becomes 92, but 93.5 becomes 94) — a detail that surprises everyone the first time a formatted grade or price looks "wrong". The lesson is not the rounding rule itself; it is that formatting is a real transformation with rules worth checking, never a cosmetic afterthought.
Worked example 1 — cleaning and parsing a messy record
Real data arrives messy. Take the string " Alice, 95 , Math " — a name, a score and a subject with stray spaces. Clean it step by step:
raw = " Alice, 95 , Math "
parts = raw.split(",")
print(parts)
clean = [p.strip() for p in parts]
print(clean)
name = clean[0]
score = int(clean[1])
subject = clean[2]
print(f"{name} scored {score} in {subject}.")
Output:
[' Alice', ' 95 ', ' Math ']
['Alice', '95', 'Math']
Alice scored 95 in Math.
Walk through it: split(",") cuts at the two commas, producing three pieces that keep their spaces. The line clean = [p.strip() for p in parts] is a list comprehension — a compact loop that strips each piece (Lesson 5 unpacks the syntax); the result is three tidy strings. Only then is it safe to convert: int(" 95 ") would actually work, because int() tolerates surrounding spaces, but int("95 ") after stripping is cleaner and the habit scales to data where spaces hide in worse places. Swap this record for a line from a real file and you have the core of every data-cleaning program ever written.
Worked example 2 — building an email address and a username
Schools and companies generate addresses from names with simple rules. Given a full name, build the parts:
full_name = "Ada Lovelace"
first, last = full_name.lower().split()
email = first[0] + last + "@school.edu"
username = first + "_" + last[:3]
print(email)
print(username)
Output:
alovelace@school.edu
ada_lov
How it works: lower() normalises case first, so "Ada" and "ADA" produce the same address. split() with no argument splits on any whitespace and returns ['ada', 'lovelace'], which unpacks into first and last in one line. Then indexing (first[0] — the initial) and slicing (last[:3] — the first three letters) carve out exactly the pieces the rules need, and + glues them together. Two traps are hiding here for real data: a one-word name would make the unpack fail, and hyphens or apostrophes in names break naive rules — professional systems spend surprising effort on exactly these edges.
Common mistakes
- Trying to edit a string in place.
s[0] = "P"raises TypeError because strings are immutable. Build a new string instead:s = "P" + s[1:]. - Off-by-one slices.
"banana"[2:5]is"nan", not"nana"— the end index is excluded. Count fence posts, not characters, when a slice surprises you. - Comparing before normalising.
"Yes" == "yes"isFalse. When comparing user input, normalise both sides withstrip().lower()first. - Forgetting that split returns a list.
"a,b".split(",")is['a', 'b'], not a string — printing it shows brackets and quotes, and trying.upper()on it raises AttributeError. - Discarding a method's result.
name.upper()computes a new string and throws it away if you do not store it:name = name.upper(). Nothing about the original changes on its own.
Real-World Challenge
The task. Write an acronym maker: given a phrase like "as soon as possible", produce its acronym "ASAP" — the first letter of each word, uppercased. Then apply it to a list of phrases and print each phrase beside its acronym.
Worked solution.
def acronym(phrase):
words = phrase.split()
letters = [word[0].upper() for word in words]
return "".join(letters)
phrases = ["as soon as possible", "for your information", "laughing out loud"]
for p in phrases:
print(p, "->", acronym(p))
Output:
as soon as possible -> ASAP
for your information -> FYI
laughing out loud -> LOL
How it works. The function splits the phrase into words, takes word[0] — the first character of each — uppercases it in the comprehension, and glues the letters with "".join(...): joining with an empty string means no separator. Wrapping the logic in a function (Lesson 4's topic, previewed here) means the loop at the bottom stays readable: for each phrase, print the phrase, an arrow, and its acronym. The design generalises — the same split–transform–join shape cleans data, builds initials for a class register, and generates hashtags.
Extension. Real phrases arrive messy: extra spaces, capitals, punctuation. Upgrade the function to handle " See you LATER! " correctly (it should print SYL): strip and lower first, and strip punctuation from each word's edges before taking its first letter. Test it on at least three messy inputs and compare against hand-computed answers.
Going further — splitting and joining, the text workhorses
Real text arrives messy: log lines, pasted records, exported rows. The splitting and joining pair handles most of it. str.split() with no arguments splits on runs of any whitespace and discards the empties — exactly what ragged real-world spacing needs — and it accepts a maximum number of splits, which lets you peel the first fields off a line while keeping the remainder intact:
line = "2026-10-22 INFO User Ada logged in"
parts = line.split(None, 2)
print(parts)
words = ["alpha", "school", "python"]
print("-".join(words))
print(" ".join(reversed(words)))
Output:
['2026-10-22', 'INFO', 'User Ada logged in']
alpha-school-python
python school alpha
The maxsplit of 2 produced three parts, and the third kept its internal spaces — the date and level are fields; the message is prose, and splitting it further would be vandalism. join is split's mirror: it glues a list of strings with a separator, and it is the *right* way to build strings from pieces. Repeatedly concatenating with + inside a long loop copies the growing string every iteration; collecting parts in a list and joining once at the end is both cleaner and faster, a pattern you will meet in every text-processing codebase. Together with slicing and the methods from the main lesson, split and join cover the overwhelming majority of everyday string surgery.
Practice corner — formatting strings into columns
Reports align; f-string format specs do the aligning. After the colon, < and > pad a value to a width on the left or right, and a precision like .1f fixes decimal places — the machinery behind every neat table this course prints:
name, score = "Ada", 92.5
print(f"{name:<6}|{score:>5.1f}")
Output: Ada | 92.5 — the name occupies six characters (three letters, three spaces of padding), the score takes five with one decimal. Specs compose with everything you already know: {value:>8.2f} right-aligns money in a column, {n:,} adds thousands separators, {p:.0%} renders 0.625 as 63%. When output must line up — receipts, leaderboards, logs — reach for the format spec instead of counting spaces by hand.
🎬 Watch & Learn
Prefer video? These searches surface top tutorials on this lesson's topic:
- Python strings & string methods — complete tutorial (freeCodeCamp style) — pick a recent, well-reviewed video
- Corey Schafer: Python strings — working with textual data — pick a recent, well-reviewed video
- Tech With Tim: Python string methods you must know — pick a recent, well-reviewed video
💪 Practice Zone
Try these yourself — hints are included, answers are not. Run your code in any Python environment.
- Name formatter — A user typed
" aLiCe jOnEs ". Clean it up and printAlice Jones.Hint
Chain two methods:.strip()removes spaces,.title()capitalizes each word. - Simple email check — Ask for an email address. Print
Looks validif it contains both@and., elseInvalid email.Hint
Theinoperator tests substrings:"@" in email. - Word counter — Ask the user for a sentence and print how many words it has.
Hint
sentence.split()breaks text into a list of words — then uselen(). - Password strength check — Ask for a password. Print
Strongif it is at least 8 characters long AND contains a digit, elseWeak.Hint
Check the digit withany(c.isdigit() for c in password).
🌍 Real World
When you sign up for any website, string methods validate your input behind the scenes: .strip() trims accidental spaces, .lower() normalizes your email, and checks like "@" in email reject typos. The password-strength rules you practiced are the same ones guarding real accounts.