πŸ”€
CodingπŸŽ“ Ages 14-18Intermediate 13 min read

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, so greeting still prints hello.
  • 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 call join on, which surprises many beginners.
  • Every item in the list must already be a string, or join raises a TypeError.

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 lowercase cats swapped β€” but note "Cats" (capital C) is left untouched because matching is case-sensitive.
  • find("Cats") returns the index 13 of the first match; find("birds") returns -1 to 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
  • startswith and endswith check the beginning or end β€” great for spotting file types or prefixes.
  • isdigit() is True only when every character is a digit, so it is a quick check before converting text to a number with int().
  • 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:

  1. raw.split("\n") breaks the block into a list of individual lines.
  2. For each line, strip() removes stray spaces and title() fixes the capitalisation in one chained step.
  3. if name: skips empty strings, which count as False, so blank lines are dropped.
  4. sort() puts the cleaned names in alphabetical order, and join presents 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() then lower() to normalise it.
  • Print whether it startswith("cool") and whether it endswith("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?

What does "a,b,c".split(",") produce?

Why does s.replace("x", "y") not change the variable s by itself?

What does "-".join(["2026", "05", "30"]) return?

What does "007".isdigit() return?

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.