Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Python
  3. Python String Methods Practice: f-strings, split, join, strip, slicing
Python53 exercises

Python String Methods Practice: f-strings, split, join, strip, slicing

Practice Python string methods: split() with sep=None vs explicit separator, why strip() removes too much, f-string padding/alignment syntax, and the join() TypeError fix.

Common ErrorsQuick ReferencePractice
Warm-up1 / 2

Can you write this from memory?

Write an expression that splits `item` by commas into a list of strings

On this page
  1. 1split(): with vs without an argument
  2. 2strip(): it's a character set, not a substring
  3. 3join(): all items must be strings
  4. 4f-strings: the format spec mini-language
  5. Alignment
  6. Number formatting
  7. Debug syntax (Python 3.8+)
  8. 5Slicing: start:stop:step
  9. 6splitlines(): the newline-safe split
  10. 7Substring search: in, find(), index()
  11. The in operator (existence check)
  12. find() vs index()
  13. 8String validation: isdigit(), isalpha(), etc.
  14. 9Raw strings: r"..." for regex and paths
  15. 10Case-insensitive comparison: lower() vs casefold()
  16. 11Common error messages
  17. TypeError: sequence item 0: expected str instance, int found
  18. IndexError: string index out of range
  19. TypeError: 'str' object does not support item assignment
  20. 12Quick recipes
  21. Clean and split CSV-like input
  22. Check multiple extensions
  23. Title case with exceptions
  24. Multiline f-strings
  25. Replace with count limit
  26. Replace multiple characters
  27. 13References
split(): with vs without an argumentstrip(): it's a character set, not a substringjoin(): all items must be stringsf-strings: the format spec mini-languageSlicing: start:stop:stepsplitlines(): the newline-safe splitSubstring search: in, find(), index()String validation: isdigit(), isalpha(), etc.Raw strings: r"..." for regex and pathsCase-insensitive comparison: lower() vs casefold()Common error messagesQuick recipesReferences

In Python, was it split() or split(" ")? How do you right-pad with f-strings? Why did rstrip(".txt") delete too much?

Python string methods are individually simple, but there are dozens of them, and the details blur together. These exercises focus on the patterns that actually trip people up: the split() whitespace behavior, the strip() character-set gotcha, f-string formatting syntax, and the join() TypeError.

Quick reference:

  • split() → groups whitespace, strips ends
  • split(" ") → literal space only, keeps empties
  • strip("abc") → removes any of a, b, c (not the substring "abc")
  • removeprefix("abc") → removes exact prefix (Python 3.9+)
  • f"{x:>10}" → right-align in 10 chars
Related Python Topics
Python Loops: range(), enumerate(), zip(), break/continue, for-elsePython Function Arguments: defaults, *args, keyword-only, **kwargsPython Collections: Lists, Dicts, Sets & TuplesPython Comprehensions: List, Dict, Set & Generator Expressions

split() groups whitespace and strips ends. split(" ") splits on literal spaces, creating empties from consecutive spaces. Use split() for words, split(sep) for structured data.

For a quick copy-paste reference of every method covered here, see the Python string methods cheat sheet.

The most common string confusion in Python:

"a  b  c".split()      # ['a', 'b', 'c'] — whitespace grouped
"a  b  c".split(" ")   # ['a', '', 'b', '', 'c'] — literal space

When sep is not specified (or is None):

  1. Runs of consecutive whitespace are treated as one separator
  2. Leading and trailing whitespace is stripped
  3. The result never has empty strings at the ends

When sep is specified:

  1. Each occurrence of the separator creates a split
  2. Consecutive separators create empty strings
  3. Leading/trailing separators create empty strings at the ends
"  hello world  ".split()      # ['hello', 'world']
"  hello world  ".split(" ")   # ['', '', 'hello', 'world', '', '']

Rule of thumb:

  • Use split() for human-written text with arbitrary whitespace
  • Use split(sep) for structured data (CSV, logs) where empty fields matter
  • For multiple delimiters, use re.split()

Ready to practice?

Start practicing Python String Methods: f-strings, split, join, strip, slicing with spaced repetition

strip("abc") removes any of a, b, c from the ends—not the substring "abc". For prefix/suffix removal, use removeprefix()/removesuffix() (Python 3.9+).

This trips up almost everyone:

"test.txt".rstrip(".txt")   # "tes" — not "test"!

Why? strip(), lstrip(), and rstrip() treat the argument as a set of characters to remove from the ends. They keep removing any character that appears in the set until they hit one that doesn't.

"aabbcc".strip("abc")  # "" — all chars are in the set!
"file.txt".rstrip(".txt")  # "file" if lucky, less if unlucky

The fix: Use removeprefix() and removesuffix() (Python 3.9+):

"test.txt".removesuffix(".txt")  # "test"
"test.txt".removeprefix("test")  # ".txt"

For older Python, use slicing with a check:

s = "test.txt"
if s.endswith(".txt"):
    s = s[:-4]

join() raises TypeError on non-strings. Fix with ",".join(map(str, items)). This is intentional—Python prefers explicit conversion over silent coercion.

join() is the inverse of split(), but it has a strict requirement:

",".join(["a", "b", "c"])  # "a,b,c"
",".join([1, 2, 3])        # TypeError: sequence item 0: expected str instance, int found

The fix: Convert all items to strings first:

# Option 1: map (slightly faster)
",".join(map(str, items))

# Option 2: generator expression
",".join(str(x) for x in items)

# Option 3: list comprehension
",".join([str(x) for x in items])

This is intentional design—Python prefers explicit conversion over surprising coercion.


f-strings use a powerful formatting syntax after the colon:

f"{value:[[fill]align][width][,][.precision][type]}"

Alignment

SpecMeaningExample
<Left-alignf"{x:<10}"
>Right-alignf"{x:>10}"
^Centerf"{x:^10}"

Add a fill character before the alignment:

f"{'hi':_<10}"   # "hi________"
f"{'hi':_>10}"   # "________hi"
f"{'hi':_^10}"   # "____hi____"

Number formatting

n = 1234567
f"{n:,}"         # "1,234,567" — thousands separator
f"{n:_}"         # "1_234_567" — underscore separator

n = 42
f"{n:05d}"       # "00042" — zero-pad to 5 digits
f"{n:+d}"        # "+42" — always show sign

x = 3.14159
f"{x:.2f}"       # "3.14" — 2 decimal places
f"{x:10.2f}"     # "      3.14" — width 10, 2 decimals

Debug syntax (Python 3.8+)

The = specifier prints both the expression and its value:

x = 42
print(f"{x=}")        # x=42
print(f"{x = }")      # x = 42 (spaces preserved)
print(f"{x*2 = }")    # x*2 = 84 (works with expressions)
print(f"{x = :.2f}")  # x = 42.00 (with format spec)

This replaces the common pattern of writing f"x = {x}" for debugging.


String slicing uses the same syntax as lists:

s = "Python"
s[0:3]    # "Pyt" — indices 0, 1, 2
s[3:]     # "hon" — from index 3 to end
s[:3]     # "Pyt" — from start to index 3
s[::2]    # "Pto" — every 2nd character
s[::-1]   # "nohtyP" — reversed

Key insight: Slicing never raises IndexError. Out-of-bounds indices are silently clamped:

s = "hi"
s[0:100]  # "hi" — not an error
s[100:]   # "" — empty string
s[5]      # IndexError! — indexing does raise

Reverse a string:

"hello"[::-1]  # "olleh"

The -1 step means "go backwards by 1".


split("\n") has two problems:

  1. It doesn't handle Windows line endings (\r\n)
  2. A trailing newline creates an empty string at the end
"a\nb\n".split("\n")    # ['a', 'b', '']
"a\nb\n".splitlines()   # ['a', 'b']

"a\r\nb".split("\n")    # ['a\r', 'b'] — \r left behind
"a\r\nb".splitlines()   # ['a', 'b'] — handles all endings

splitlines() recognizes: \n, \r\n, \r, and several others.

Pass keepends=True if you need the line endings:

"a\nb".splitlines(keepends=True)  # ['a\n', 'b']

"Does this string contain X?" is one of the most common string operations.

The in operator (existence check)

if "cat" in "concatenate":
    print("Found it!")  # This runs

Use in when you only need yes/no. It's readable and fast.

find() vs index()

Both return the position of a substring:

"hello".find("ll")   # 2
"hello".index("ll")  # 2

"hello".find("x")    # -1 (not found)
"hello".index("x")   # ValueError: substring not found

When to use which:

  • find(): when "not found" is a valid case you'll handle
  • index(): when "not found" should crash (it's a bug)
# find() pattern: check the return value
pos = text.find(target)
if pos != -1:
    # Found at position pos
    ...

# index() pattern: let it crash if missing
pos = text.index(target)  # Bug if not found

Right-to-left: rfind() and rindex() search from the end.


For validating user input:

MethodTrue forFalse for
isdigit()"123""12.3", "12a", ""
isalpha()"abc", "абв""abc1", "a b", ""
isalnum()"abc123""abc 123", ""
isnumeric()"123", "½", "²""12.3", ""
isspace()" \t\n""a ", ""
user_input = input("Enter age: ")
if user_input.isdigit():
    age = int(user_input)
else:
    print("Please enter a number")

Note: All return False for empty strings. isnumeric() is broader than isdigit()—it includes Unicode numerals like ½ and ².


Backslashes are escape characters in normal strings:

print("Line1\nLine2")   # Prints on two lines
print("C:\\Users\\name")  # Need double backslashes

Raw strings treat backslashes as literal:

print(r"Line1\nLine2")  # Prints: Line1\nLine2
print(r"C:\Users\name")  # Prints: C:\Users\name

Use raw strings for:

  1. Regex patterns: \d means "digit" in regex, but Python sees \d as an escape. For a deeper look at regex syntax, see the Python regex cheat sheet.

    import re
    re.findall(r"\d+", "abc123")  # ['123']
    re.findall("\\d+", "abc123")  # Same, but ugly
    
  2. Windows paths:

    path = r"C:\Users\name\Documents"
    

Gotcha: Raw strings can't end with an odd number of backslashes (use regular string or add space).


For ASCII text, lower() works fine:

"Hello".lower() == "hello"  # True

For international text, use casefold():

# German sharp S
"ß".lower()     # "ß" — still sharp S
"ß".casefold()  # "ss" — proper lowercase

# Case-insensitive comparison
"STRASSE".casefold() == "straße".casefold()  # True
"STRASSE".lower() == "straße".lower()        # False!

Rule: Use casefold() for case-insensitive comparison of user input. Use lower() when you specifically want ASCII-only lowercasing. If you work across languages, Python vs JavaScript string handling covers the key differences in how each language approaches string operations.


TypeError: sequence item 0: expected str instance, int found

From join() with non-strings. Fix: ",".join(map(str, items))

IndexError: string index out of range

From indexing (not slicing) beyond the string length. Fix: check len() first, or use slicing which doesn't raise.

TypeError: 'str' object does not support item assignment

Strings are immutable. You can't do s[0] = "X". Fix: build a new string with slicing or replace().


Clean and split CSV-like input

text = "  Alice, Bob ,Carol "
names = [s.strip() for s in text.split(",")]
# ['Alice', 'Bob', 'Carol']

Check multiple extensions

if filename.endswith((".jpg", ".png", ".gif")):
    process_image(filename)

Title case with exceptions

s = "the quick BROWN fox"
s.title()      # "The Quick Brown Fox" — capitalizes every word
s.capitalize() # "The quick brown fox" — first char only

Multiline f-strings

# Use parentheses for implicit concatenation
message = (
    f"Hello {name}, "
    f"you have {count} items "
    f"worth {total:.2f}"
)

# Or triple quotes for literal newlines
message = f"""
Hello {name},
You have {count} items.
"""

Replace with count limit

# Replace all occurrences (default)
"a-b-c-d".replace("-", "_")     # "a_b_c_d"

# Replace only first N occurrences
"a-b-c-d".replace("-", "_", 2)  # "a_b_c-d"

Replace multiple characters

# For a few characters, chain replace:
s.replace("a", "").replace("b", "").replace("c", "")

# For many, use str.translate:
s.translate(str.maketrans("", "", "abc"))

  • Python Tutorial: Strings
  • Common string operations (str methods)
  • Format String Syntax
  • Format Specification Mini-Language
  • PEP 498 – Literal String Interpolation (f-strings)
  • PEP 616 – removeprefix and removesuffix

When to Use Python String Methods: f-strings, split, join, strip, slicing

  • Format dynamic output with f-strings—interpolation, padding, alignment, number formatting.
  • Parse and clean user input with split/strip/replace.
  • Build strings from sequences with join (remember: all items must be strings).
  • Slice for substrings, including reverse with [::-1].
  • Use splitlines() for cross-platform newline handling.

Check Your Understanding: Python String Methods: f-strings, split, join, strip, slicing

Prompt

Given " Alice, Bob ,Carol ", return ["Alice", "Bob", "Carol"].

What a strong answer looks like

Use split(",") and strip() on each piece: `[s.strip() for s in text.split(",")]`. Note: split(",") keeps the spaces; strip() on each item removes them. Don't use split() without args here—it would split on spaces too.

What You'll Practice: Python String Methods: f-strings, split, join, strip, slicing

f-string formatting (interpolation, padding, alignment)Format spec: fill, align (<>^), width, precisionf-string debug syntax f"{var=}" (Python 3.8+)split() vs split(sep) behaviorstrip/lstrip/rstrip (character set, not substring)removeprefix/removesuffix (Python 3.9+)join() and the all-strings requirementsplitlines() for cross-platform newlinesString slicing (start:stop:step, reverse)Substring search: in, find(), index()Validation: isdigit(), isalpha(), isalnum(), isnumeric()replace() with optional count parameterlower() vs casefold() for case-insensitive comparisonRaw strings r"..." for regex and Windows pathsstartswith/endswith with tuplesMulti-line strings and escape sequences

Common Python String Methods: f-strings, split, join, strip, slicing Pitfalls

  • Strings are immutable—methods return new strings, they don't modify in place
  • Index out of range errors—use slicing (never raises) or check len() first
  • Forgetting to call the method—`s.lower` vs `s.lower()`
  • strip() treats arg as character set, not substring—use removeprefix/removesuffix
  • join() requires all strings—use map(str, items) for mixed types
  • split() vs split(" ")—different whitespace handling (see FAQ)
  • split("\n") leaves empty string at end—use splitlines() instead

Python String Methods: f-strings, split, join, strip, slicing FAQ

Why does split() behave differently with no argument?

`split()` (no args) treats runs of whitespace as a single separator AND strips leading/trailing whitespace. `split(" ")` splits on literal spaces only—consecutive spaces create empty strings, and leading/trailing spaces create empty items. Use `split()` for whitespace-separated words; use `split(sep)` for structured data like CSV.

Why doesn't rstrip(".txt") work like I expect?

`strip()`/`lstrip()`/`rstrip()` treat the argument as a **set of characters**, not a substring. `rstrip(".txt")` removes any trailing t, x, or . characters. For actual prefix/suffix removal, use `removeprefix()` / `removesuffix()` (Python 3.9+), or slice with `endswith()` check.

How do I pad/align with f-strings?

Use the format spec after a colon: `f"{x:>10}"` right-aligns in 10 chars, `f"{x:<10}"` left-aligns, `f"{x:^10}"` centers. Add a fill character before the alignment: `f"{x:_>10}"` pads with underscores. For numbers: `f"{n:05d}"` zero-pads to 5 digits, `f"{n:,}"` adds thousand separators.

Why does join() give TypeError: expected str instance, int found?

`join()` requires all items to be strings. Fix: `",".join(map(str, items))` or `",".join(str(x) for x in items)`. This is intentional—explicit conversion prevents surprising string coercion.

Are strings mutable in Python?

No. Methods like replace() or lower() return new strings rather than modifying in place. This means you need to reassign: `s = s.lower()`, not just `s.lower()`.

When should I use f-strings vs .format()?

Use f-strings for most cases—they're faster and more readable. Use `.format()` when: (1) the template is stored separately from values, (2) you need to reuse a template with different values, or (3) you're on Python < 3.6. Both use the same underlying format spec syntax.

What's f"{var=}" (the debug syntax)?

Python 3.8+ lets you write `f"{x=}"` which prints both the name and value: `x=42`. Great for quick debugging. You can add format specs too: `f"{x=:.2f}"`. Spaces around `=` are preserved: `f"{x = }"` prints `x = 42`.

When should I use splitlines() vs split("\n")?

`splitlines()` handles all line-ending styles (\n, \r\n, \r) and doesn't leave an empty string at the end. `split("\n")` only splits on \n and creates empty strings from trailing newlines. Use `splitlines()` for text files; use `split("\n")` only when you specifically want \n behavior.

How do I reverse a string?

Use slice notation: `s[::-1]`. The -1 step traverses backwards. This works because strings are sequences in Python.

How do I check if a string contains a substring?

Use the `in` operator: `if "cat" in text:`. For the position, use `find()` (returns -1 if not found) or `index()` (raises ValueError if not found). Prefer `in` for existence checks, `find()` when you need the position and missing is expected, `index()` when missing is a bug.

What's the difference between find() and index()?

Both return the position of a substring. `find()` returns -1 if not found; `index()` raises ValueError. Use `find()` when missing is a valid case (check `!= -1`), `index()` when missing should crash. Same pattern: `rfind()`/`rindex()` search from the right.

How do I check if a string is all digits/letters?

`isdigit()` checks for digits (0-9). `isalpha()` checks for letters. `isalnum()` checks for both. `isnumeric()` is broader than `isdigit()` (includes ½, ²). For validating integer input, `isdigit()` is usually what you want. All return False for empty strings.

When should I use raw strings (r"...")?

Use raw strings when backslashes should be literal: regex patterns (`r"\d+"`) and Windows paths (`r"C:\Users\name"`). In raw strings, `\` is just a backslash, not an escape. Without `r`, you'd need `"\\d+"` for the same regex.

What's the difference between lower() and casefold()?

`lower()` lowercases ASCII letters. `casefold()` is more aggressive—it handles international characters like German ß → ss. Use `casefold()` for case-insensitive comparison of user input that might contain non-ASCII text.

Python String Methods: f-strings, split, join, strip, slicing Syntax Quick Reference

f-string basics
name = "Alice"
greeting = f"Hello, {name}!"
f-string padding/alignment
f"{x:>10}"   # Right-align in 10 chars
f"{x:<10}"   # Left-align
f"{x:^10}"   # Center
f"{x:_>10}"  # Pad with underscores
f-string numbers
f"{n:05d}"   # Zero-pad to 5 digits
f"{n:,}"     # Thousands separator: 1,234,567
f"{x:.2f}"   # Two decimal places: 3.14
f-string debug (3.8+)
x = 42
print(f"{x=}")      # x=42
print(f"{x = }")    # x = 42
print(f"{x*2 = }")  # x*2 = 84
split() vs split(" ")
"a  b  c".split()      # ['a', 'b', 'c']
"a  b  c".split(" ")   # ['a', '', 'b', '', 'c']
strip() gotcha
"test.txt".rstrip(".txt")   # "tes" (oops!)
"test.txt".removesuffix(".txt")  # "test" (correct)
join() with non-strings
nums = [1, 2, 3]
",".join(map(str, nums))  # "1,2,3"
Slicing (including reverse)
text = "Python"
text[:3]     # "Pyt"
text[3:]     # "hon"
text[::-1]   # "nohtyP"
splitlines() vs split("\n")
"a\nb\n".split("\n")    # ['a', 'b', '']
"a\nb\n".splitlines()   # ['a', 'b']
startswith/endswith tuple
filename.endswith((".jpg", ".png", ".gif"))
Substring search
"cat" in "concatenate"     # True
"hello".find("ll")         # 2
"hello".find("x")          # -1 (not found)
"hello".index("x")         # ValueError!
String validation
"123".isdigit()    # True
"abc".isalpha()    # True
"abc123".isalnum() # True
"".isdigit()       # False (empty)
Raw strings
r"\d+"              # Regex: literal \d+
r"C:\Users\name"   # Windows path
casefold() vs lower()
"ß".lower()     # "ß"
"ß".casefold()  # "ss" (for comparison)
replace() with count
"a-b-c-d".replace("-", "_", 2)  # "a_b_c-d"

Python String Methods: f-strings, split, join, strip, slicing Sample Exercises

Example 1Difficulty: 2/5

Write an expression that slices `item` from index `` up to (but not including) ``

item[:]
Example 2Difficulty: 1/5

Write an expression that gets the character at index `` from `item`

item[]
Example 3Difficulty: 2/5

Write a slice that takes every other character from `item`, starting at index ``

item[::2]

+ 50 more exercises

Quick Reference
Python String Methods: f-strings, split, join, strip, slicing Cheat Sheet →

Copy-ready syntax examples for quick lookup

Also in Other Languages

JavaScript String Methods PracticeRust Strings Practice: String vs &str, Ownership & Methods

Start practicing Python String Methods: f-strings, split, join, strip, slicing

Free daily exercises with spaced repetition. No credit card required.

← Back to Python Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.