Python Tutorial
Python String Methods
Strings ship with dozens of built-in methods for cleaning, searching, splitting, and reformatting text. The golden rule: they never change the original string — they hand you back a new one.
Methods return new strings
Strings are immutable — once created, their characters can't be edited in place. So every method that "changes" a string actually returns a brand-new one and leaves the original untouched. If you don't capture the return value, the work is thrown away.
Example
name = "luna"
name.upper() # returns "LUNA" — but we ignored it
print(name) # luna (unchanged!)
name = name.upper() # capture the result
print(name) # LUNAThis is the single most common beginner bug with strings: calling text.strip() ortext.replace(...) and expecting text to change. Always assign the result somewhere.
Changing case
s = "Hello, World"
print(s.upper()) # HELLO, WORLD
print(s.lower()) # hello, world
print(s.title()) # Hello, World (each word capitalised)
print(s.capitalize()) # Hello, world (only the first char)
print(s.swapcase()) # hELLO, wORLDFor case-insensitive comparisons prefer casefold() over lower() — it handles tricky characters like the German ß: "Straße".casefold() gives "strasse", while.lower() leaves the ß alone.
Searching and testing
Use in for a quick yes/no, find() for a position (or -1 if missing), andcount() to tally occurrences. The is…() family answers questions about the content.
s = "banana bread"
print("bread" in s) # True
print(s.find("bread")) # 7 (index where it starts)
print(s.find("cake")) # -1 (not found — NOT an error)
print(s.count("a")) # 3
print(s.startswith("ban")) # True
print(s.endswith(".txt")) # False
print("42".isdigit()) # True
print("hello".isalpha()) # True
print("Order 42".isalnum())# False (the space isn't alphanumeric)find() returns -1 when the substring is missing; index() does the same job but raises a ValueError instead. Use find() when "not there" is a normal case.
Cleaning up whitespace and characters
raw = " Hello "
print(repr(raw.strip())) # 'Hello' (both ends)
print(repr(raw.lstrip())) # 'Hello ' (left only)
print(repr(raw.rstrip())) # ' Hello' (right only)
print("hello".replace("l", "L")) # heLLo (every match)
print("a.b.c".replace(".", "/")) # a/b/cstrip() removes characters, not a suffix. "hello.txt".strip(".txt") doesnot give "hello" — it strips any of the characters ., t,x, l from both ends and returns "hello" by luck here, but"text.txt".strip(".txt") gives just "e"! Use removesuffix(".txt") instead.
Splitting and joining
split() turns a string into a list; join() turns a list back into a string. They are the two halves of almost every text-processing task.
csv = "Ada,Grace,Alan"
parts = csv.split(",") # ['Ada', 'Grace', 'Alan']
print(parts)
print("-".join(["2024", "01", "15"])) # 2024-01-15
# split() with no argument splits on ANY run of whitespace
print(" the quick fox ".split()) # ['the', 'quick', 'fox']The separator "owns" join: you write separator.join(list), e.g.", ".join(names). A frequent mistake is names.join(", "), which is backwards.
Padding and alignment
print("7".zfill(3)) # 007 (zero-pad to width 3)
print("cat".center(7, "*")) # **cat**
print("hi".ljust(5, ".")) # hi...
print("hi".rjust(5, ".")) # ...hiPutting it together
A tiny, realistic normaliser — tidy up a messy name a user typed into a form:
messy = " aDA LoVeLaCe "
clean = messy.strip().title()
print(clean) # Ada Lovelace
print(" ".join(clean.split())) # Ada Lovelace (collapse inner spaces)Because each method returns a new string, you can chain them left to right: strip first, thentitle, then split/join to squeeze out the double spaces.
Common methods at a glance
| Method | Does | Example → result |
|---|---|---|
upper() / lower() | Change case | "Hi".upper() → "HI" |
strip() | Trim whitespace both ends | " x ".strip() → "x" |
replace(a, b) | Swap every a for b | "a-b".replace("-","_") → "a_b" |
split(sep) | String → list | "a,b".split(",") → ['a','b'] |
sep.join(list) | List → string | ",".join(['a','b']) → "a,b" |
find(sub) | Index or -1 | "abc".find("c") → 2 |
count(sub) | How many times | "aaa".count("a") → 3 |
startswith(p) | Boolean prefix test | "file.py".startswith("file") → True |
Try It Yourself
Exercise: A user typed " JOHN.SMITH@Example.COM ". Produce the clean lowercase email john.smith@example.com.
Show solution
raw = " JOHN.SMITH@Example.COM "
print(raw.strip().lower()) # john.smith@example.comKey Takeaways
- String methods return new strings — capture the result, or the change is lost.
find()returns-1when missing;index()raises instead.split()/join()are the workhorses of text processing; the separator ownsjoin.strip()removes characters, not suffixes — useremovesuffix()for that.
📘 Real-World Deep Dive
<code>str</code> ships with ~50 methods covering case, search, split/join, padding, replace, and translation. Mastering them by heart replaces dozens of hand-written regex-based helpers.
Real-Life Scenario
A small key/value parser that handles <code>"name=value"</code>, RFC-style <code>Name: Value</code>, and multi-line INI-style <code>name = value</code> configs using only <code>str</code> methods.
Real-Life Example
def parse_kv(line: str, sep: str = "=", *, key_case="lower") -> tuple[str, str] | None:
s = line.strip()
if not s or s.startswith("#"):
return None
if sep not in s:
return None
k, _, v = s.partition(sep)
k, v = k.strip(), v.strip().strip('"').strip("'")
if key_case == "lower": k = k.lower()
if key_case == "upper": k = k.upper()
return k, v
samples = [
"USER=ada",
' message = "Hello, World!" ',
"# comment",
"no-separator-here",
"PORT = 5432",
"PATH=/usr/local/bin:/usr/bin",
]
for line in samples:
parsed = parse_kv(line)
print(f"{line!r:<35} -> {parsed}")Expected Output
'USER=ada' -> ('user', 'ada')
' message = "Hello, World!" ' -> ('message', 'Hello, World!')
'# comment' -> None
'no-separator-here' -> None
'PORT = 5432' -> ('port', '5432')
'PATH=/usr/local/bin:/usr/bin' -> ('path', '/usr/local/bin:/usr/bin')Common mistakes
str.partition(sep)returns(head, sep, tail); ifsepis missing,tailis empty but the tuple is always 3-elements.str.split()with no sep splits on any whitespace *runs*;str.split(" ")splits on every single space.str.removesuffix(suf)(Python 3.9+) only removes one occurrence — not regular expressions.
🚀 Performance & Best Practices
- For case-insensitive membership,
"foo" in s.casefold()pre-lowercases once. str.startswith(("http://", "https://"))accepts a tuple — much cheaper thanor-chains.- When matching many prefixes, build a
trieor usere.compilewith alternation.
🧪 Try It Yourself
- Refactor
parse_kvto also handle JSON-style quoted keys ("user" = "ada"). - Add a
multi_separgument that tries several separators in order. - Write
s.split_quoted()that splits on whitespace but preserves quoted spans.