Python String Methods: A Practical Reference for Everyday Text Processing
- Strings are immutable; every method returns a new string and leaves the original unchanged.
find()returns -1 on failure;index()raisesValueError— choose based on whether absence is an error."sep".join(iterable)is faster than repeated+concatenation for building strings from a list.str.translate(table)withstr.maketrans()handles bulk character replacements in a single pass.
Searching: find, index, count, startswith, endswith, in
| Method | Returns | On failure |
|---|---|---|
find(sub) | Lowest index of sub | -1 |
rfind(sub) | Highest index of sub | -1 |
index(sub) | Lowest index of sub | ValueError |
rindex(sub) | Highest index of sub | ValueError |
count(sub) | Number of non-overlapping occurrences | 0 |
startswith(prefix) | True / False | False |
endswith(suffix) | True / False | False |
text = "the quick brown fox jumps over the lazy dog"
print(text.find("fox")) # 16
print(text.find("cat")) # -1
print(text.count("the")) # 2
print(text.startswith("the")) # True
print(text.endswith("cat")) # False
# in operator: cleanest membership test
print("fox" in text) # True
Use find() when a missing substring is a normal case (such as optional URL parameters). Use index() when absence would be a programming error worth raising an exception over.
Both startswith and endswith accept a tuple of prefixes/suffixes to test against multiple options at once:
filename = "report.pdf"
print(filename.endswith((".pdf", ".PDF"))) # True
Splitting and joining: split, rsplit, partition, join
| Method | Description |
|---|---|
split(sep, maxsplit) | Split from the left on sep (default: any whitespace) |
rsplit(sep, maxsplit) | Split from the right |
splitlines() | Split on line endings; handles CR, LF, and CRLF |
partition(sep) | Returns (before, sep, after) — exactly 3 parts |
join(iterable) | Join an iterable of strings with self as separator |
line = "name=Alice,age=30,city=London"
parts = line.split(",")
# ['name=Alice', 'age=30', 'city=London']
key, sep, value = "name=Alice".partition("=")
# key='name', sep='=', value='Alice'
words = ["Python", "is", "great"]
sentence = " ".join(words)
# 'Python is great'
# rsplit with maxsplit: useful for parsing paths
path = "/home/user/docs/report.pdf"
head, _, tail = path.rpartition("/")
# head='/home/user/docs', tail='report.pdf'
Building a long string by concatenating with + inside a loop creates a new string object on every iteration. Collect parts into a list first and join at the end — it is significantly faster for large numbers of pieces.
Stripping whitespace and characters: strip, lstrip, rstrip
| Method | Effect |
|---|---|
strip(chars) | Remove leading and trailing chars (default: whitespace) |
lstrip(chars) | Remove leading chars only |
rstrip(chars) | Remove trailing chars only |
removeprefix(prefix) | Remove exact prefix if present (Python 3.9+) |
removesuffix(suffix) | Remove exact suffix if present (Python 3.9+) |
raw = " hello world "
print(raw.strip()) # "hello world"
print(raw.lstrip()) # "hello world "
print(raw.rstrip()) # " hello world"
# chars argument: strips any combination of those characters
messy = "...!!hello!...!!!"
print(messy.strip(".!")) # "hello"
# removeprefix / removesuffix (Python 3.9+)
url = "https://example.com"
print(url.removeprefix("https://")) # "example.com"
filename = "report.pdf"
print(filename.removesuffix(".pdf")) # "report"
Note that strip(chars) removes any combination of the listed characters, not the literal substring. Use removeprefix / removesuffix when you need to match an exact sequence.
Replacing: replace, translate
text = "aabbccaabb"
print(text.replace("aa", "X")) # "XbbccXbb"
print(text.replace("aa", "X", 1)) # "Xbbccaabb" (max 1 replacement)
replace is straightforward for simple substitutions. For bulk single-character replacements, translate with maketrans is much faster because it operates in a single pass over the string:
table = str.maketrans("aeiou", "AEIOU")
print("hello world".translate(table))
# "hEllO wOrld"
# Delete characters by passing None as the mapping value
delete_digits = str.maketrans("", "", "0123456789")
print("ph0n3 numb3r".translate(delete_digits))
# "phn numbr"
Case conversion: upper, lower, title, capitalize, swapcase
| Method | Example input | Example output |
|---|---|---|
upper() | hello world | HELLO WORLD |
lower() | HELLO WORLD | hello world |
capitalize() | hello world | Hello world |
title() | hello world | Hello World |
swapcase() | Hello World | hELLO wORLD |
casefold() | HELLO | hello (Unicode-safe) |
s = "python is FUN"
print(s.upper()) # "PYTHON IS FUN"
print(s.lower()) # "python is fun"
print(s.title()) # "Python Is Fun"
print(s.capitalize()) # "Python is fun"
print(s.swapcase()) # "PYTHON IS fun"
Use casefold() rather than lower() for case-insensitive comparisons involving non-ASCII text. For example, the German sharp-s character ss casefolded is ss, which lower() does not handle correctly on all platforms.
Testing content: isalpha, isdigit, isalnum, isspace, isidentifier
| Method | Returns True if all characters are… |
|---|---|
isalpha() | Alphabetic (letters only) |
isdigit() | Digits (0–9 and Unicode digits) |
isnumeric() | Numeric (includes fractions, superscripts) |
isalnum() | Alphabetic or digit |
isspace() | Whitespace characters |
isidentifier() | A valid Python identifier |
isupper() | All cased characters are uppercase |
islower() | All cased characters are lowercase |
istitle() | Title-cased (each word starts capitalised) |
print("hello".isalpha()) # True
print("hello123".isalpha()) # False
print("123".isdigit()) # True
print("hello123".isalnum()) # True
print(" \t\n".isspace()) # True
print("my_var".isidentifier()) # True
print("2bad".isidentifier()) # False
All testing methods return False on an empty string. Always guard against empty input before relying on these methods for validation.
Method chaining and immutability
Because every string method returns a new string object, you can chain calls in a single expression:
raw = " The Quick Brown Fox "
result = raw.strip().lower().replace("fox", "cat")
print(result) # "the quick brown cat"
The original string raw is never modified. This immutability is fundamental to how Python strings work: they are interned and shared under the hood, so mutation would be unsafe. Every method call allocates a new string. For a very long chain of transformations on large strings, consider whether a single translate call or a compiled regular expression would be more efficient.
original = "hello"
modified = original.upper()
print(original) # "hello" -- unchanged
print(modified) # "HELLO" -- new object
print(original is modified) # False