Python String Methods Deep Dive
Go deeper with Python string methods: case conversion, strip, replace, split and join, find and count, startswith and endswith, plus checks like isdigit. Runnable code, a worked example and a quiz.
Key takeaways
- String methods return a NEW string β the original is never changed, because strings are immutable
- Case methods (upper, lower, title) and strip() are the everyday tools for tidying text
- split() turns a string into a list of words, and join() turns a list back into a string
- replace(), find() and count() let you search and edit text without writing loops
- Test methods like isdigit() and startswith() return True or False for validation
Strings can do more than you think
You have already met text in working with text and strings, where you learned to create strings, join them with +, and read characters by index. But strings come with a large toolbox of built-in methods β small functions attached to every string β that let you clean, search and reshape text without writing loops by hand. This lesson digs into the methods you will reach for again and again.
The single most important idea to hold onto: strings are immutable. A method never changes the string you call it on. Instead it builds and hands back a new string. If you want to keep the change, you must store the result.
greeting = "hello"
greeting.upper()
print(greeting) # hello (unchanged!)
greeting = greeting.upper()
print(greeting) # HELLO (now stored)
- The first
greeting.upper()does produce"HELLO", but we throw it away, sogreetingstill printshello. - The second time we assign the result back into
greeting, so the change sticks.
Changing case
The case methods are the simplest to start with. Each returns a new string.
title = "the Hidden Garden"
print(title.upper()) # THE HIDDEN GARDEN
print(title.lower()) # the hidden garden
print(title.title()) # The Hidden Garden
print(title.capitalize()) # The hidden garden
upper()makes every letter uppercase;lower()makes every letter lowercase.title()capitalises the first letter of each word β handy for names and headings.capitalize()capitalises only the first letter of the whole string and lowercases the rest.
A common use is making comparisons case-insensitive: convert both sides to lower() first so "Yes", "yes" and "YES" all match.
Trimming whitespace with strip
Text from users or files often has stray spaces or newline characters at the ends. strip() removes whitespace from both ends:
raw = " Ada Lovelace \n"
clean = raw.strip()
print(repr(clean)) # 'Ada Lovelace'
strip()removes spaces, tabs and newlines from the start and end β but leaves the space between the names alone.repr()shows the quotes and any hidden characters, which is useful while debugging.
There are also lstrip() (left side only) and rstrip() (right side only) for finer control.
Splitting and joining
split() breaks a string into a list of pieces. By default it splits on whitespace, or you can pass a separator:
sentence = "the cat sat on the mat"
words = sentence.split()
print(words) # ['the', 'cat', 'sat', 'on', 'the', 'mat']
print(len(words)) # 6
date = "2026-05-30"
parts = date.split("-")
print(parts) # ['2026', '05', '30']
sentence.split()with no argument splits on any run of whitespace β a fast way to count or process words.date.split("-")splits at every dash, giving the year, month and day as separate strings.
join() is the exact opposite: it glues a list of strings back together. Notice it is called on the separator string, with the list passed in:
parts = ["2026", "05", "30"]
print("-".join(parts)) # 2026-05-30
print("/".join(parts)) # 2026/05/30
"-".join(parts)puts a dash between each item. The separator is the string you calljoinon, which surprises many beginners.- Every item in the list must already be a string, or
joinraises aTypeError.
If you have worked with Python lists in depth, you can see split and join as the bridge between text and lists.
Searching and replacing
replace() swaps one piece of text for another (every occurrence), find() reports the position of text, and count() tells you how many times it appears:
text = "I like cats. Cats are great. cats cats cats"
print(text.replace("cats", "dogs"))
print(text.find("Cats")) # 13 (index of the first match, case-sensitive)
print(text.find("birds")) # -1 (not found)
print(text.lower().count("cats")) # 5
replace("cats", "dogs")returns a new string with every lowercasecatsswapped β but note"Cats"(capital C) is left untouched because matching is case-sensitive.find("Cats")returns the index13of the first match;find("birds")returns-1to signal "not present".text.lower().count("cats")first lowercases everything, then counts β catching all five occurrences regardless of case.
Test methods: questions that answer True or False
Some methods ask a yes/no question and return a boolean. They are perfect for validating input.
print("hello".startswith("he")) # True
print("report.pdf".endswith(".pdf")) # True
print("12345".isdigit()) # True
print("12a45".isdigit()) # False
print("Hello".isalpha()) # True
print(" ".isspace()) # True
startswithandendswithcheck the beginning or end β great for spotting file types or prefixes.isdigit()isTrueonly when every character is a digit, so it is a quick check before converting text to a number withint().isalpha()checks for letters only;isspace()checks for whitespace only.
These pair naturally with the if statements you met in if, elif and else.
Worked example: cleaning up a messy name list
Imagine a sign-up form has produced a messy block of names, one per line, with inconsistent spacing and capitalisation. Let's tidy it into a clean, sorted list.
raw = " alice \n BOB\nCharlie \n dave "
clean_names = []
for line in raw.split("\n"):
name = line.strip().title()
if name: # skip any blank lines
clean_names.append(name)
clean_names.sort()
print(clean_names) # ['Alice', 'Bob', 'Charlie', 'Dave']
print(" - ".join(clean_names)) # Alice - Bob - Charlie - Dave
How it works:
raw.split("\n")breaks the block into a list of individual lines.- For each
line,strip()removes stray spaces andtitle()fixes the capitalisation in one chained step. if name:skips empty strings, which count asFalse, so blank lines are dropped.sort()puts the cleaned names in alphabetical order, andjoinpresents them on one line.
Every tool here β split, strip, title, join β is a string method returning a new string, combined with a loop and an if.
Try it yourself
Write a small username checker:
- Start with
raw = " Cool_User_99 ". - Use
strip()thenlower()to normalise it. - Print whether it
startswith("cool")and whether itendswith("99"). - Use
replace("_", "-")to swap underscores for dashes. - Count how many digits it contains by looping over the characters and testing each with
.isdigit().
Then try splitting a full sentence into words, capitalising each one with title(), and re-joining them with a single space. When you are ready to handle text that might not convert cleanly to a number, see error handling with try/except.
Quick quiz
Test yourself and earn XP
What does " hello ".strip() return?
strip() returns a new string with whitespace removed from BOTH ends; the spaces inside are kept and the original is unchanged.
What does "a,b,c".split(",") produce?
split(",") breaks the string at every comma and returns a list of the pieces: ['a', 'b', 'c'].
Why does s.replace("x", "y") not change the variable s by itself?
Strings cannot be modified in place. replace() builds and returns a new string, so you must assign it back, e.g. s = s.replace("x", "y").
What does "-".join(["2026", "05", "30"]) return?
join() glues the list items together using the string it is called on as the separator, giving "2026-05-30".
What does "007".isdigit() return?
isdigit() returns True when every character in the string is a digit 0-9, which is the case for "007".
FAQ
In Python, strings are immutable, meaning their contents can never be altered once created. This is why every string method β upper(), strip(), replace() and the rest β leaves the original untouched and instead returns a brand-new string with the change applied. The practical consequence is that you must capture the result, for example name = name.strip(), rather than just calling name.strip() and expecting name to change. Immutability makes strings safe to share and use as dictionary keys, but it does mean you have to remember to store the result.
Because each string method returns a string, you can call the next method directly on that result, reading left to right. For example " Hello World ".strip().lower().replace(" ", "-") first removes the outer spaces, then lowercases everything, then swaps the inner space for a dash, producing "hello-world". Chaining is clean and common, but if a chain gets long or hard to read, split it across several lines with intermediate variables so a future reader (often you) can follow each step.
Keep exploring
More in Coding