Python str objects provide a fixed set of methods defined in the Python Language Reference. Because strings are immutable, every method that "modifies" a string actually returns a new string, leaving the original unchanged.
Everyday text transformation
What you’ll learn: The dozen string methods you’ll use daily — case conversion, whitespace stripping, searching, and replacing. Plus the crucial fact that strings are immutable, so methods return new strings.
How to read this tab: Run each example in the Playground and confirm the original string is unchanged. That immutability is the concept that matters most.
A string method is an action you run on text: make it uppercase, find a word, replace a phrase, strip whitespace. Because Python strings are immutable, these methods never change the original — they return a brand-new string.
The original string never changes. Every "modifying" method returns a new string. name.upper() gives you an uppercase copy; name is untouched. To keep the result, reassign: name = name.upper().
The most-used string methods
These ten methods cover the vast majority of everyday text work. Each returns a new string (or a value) — the original is never modified.
text = " Hello, World! "
# Case
print(text.upper()) # " HELLO, WORLD! "
print(text.lower()) # " hello, world! "
print("priya".capitalize())# "Priya"
print("priya kumar".title())# "Priya Kumar"
# Whitespace removal
print(text.strip()) # "Hello, World!" (both ends)
print(text.lstrip()) # "Hello, World! " (left only)
print(text.rstrip()) # " Hello, World!" (right only)
# The original is unchanged — strings are immutable
print(text) # " Hello, World! " — untouched
# Length is a function, not a method
print(len("Python")) # 6text.upper() does not change text — it returns a new string. If you want to keep the result, assign it: text = text.upper(). This trips up beginners who expect the method to modify the variable in place.
Use in for membership, not find. To check if a substring exists, "cat" in text is clearer than text.find("cat") != -1. Reserve find/index for when you need the actual position.
Searching and replacing
s = "the quick brown fox"
# Membership — use 'in', not a method
print("quick" in s) # True
# Find position (returns -1 if not found)
print(s.find("brown")) # 10
print(s.find("cat")) # -1 (not found — no error)
print(s.index("brown")) # 10 (like find, but raises ValueError if missing)
# Count occurrences
print(s.count("o")) # 2
# Replace (returns a new string)
print(s.replace("quick", "slow")) # "the slow brown fox"
# Starts / ends with
print(s.startswith("the")) # True
print(s.endswith("fox")) # True
print("file.pdf".endswith((".pdf", ".doc"))) # True — accepts a tuple✅ Beginner tab complete
- I know upper/lower/strip/replace return NEW strings (the original is unchanged)
- I can search text with in, find, and index — and know find returns -1 while index raises
- I can use startswith/endswith, including with a tuple of options
- I know len() is a function, not a method
Splitting, joining, and validation
What you’ll learn: split and join (the backbone of parsing and assembling text), plus the validation methods (isdigit, isalpha) and alignment methods (zfill, ljust, center).
How to read this tab: Master the split/join pair first — they are the two most important string operations in real code.
join is called on the separator, and reads backwards at first. ", ".join(items) — the separator owns the method because join must accept any iterable of strings. And join only works on strings: convert numbers first with str(n) or a generator expression.
Splitting and joining
Splitting turns a string into a list; joining turns a list back into a string. These two operations are the backbone of text parsing and assembly.
# split — string to list
csv = "Priya,30,Mumbai"
print(csv.split(",")) # ['Priya', '30', 'Mumbai']
# split with no argument splits on ANY whitespace, collapsing runs
print("a b c".split()) # ['a', 'b', 'c']
# maxsplit limits the number of splits
print("a-b-c-d".split("-", 1)) # ['a', 'b-c-d']
# splitlines — split on line boundaries
print("line1\nline2\nline3".splitlines()) # ['line1', 'line2', 'line3']
# join — list to string (called on the SEPARATOR)
parts = ['Priya', '30', 'Mumbai']
print(",".join(parts)) # "Priya,30,Mumbai"
print(" ".join(["Hello", "World"])) # "Hello World"
# join only works on strings — convert numbers first
nums = [1, 2, 3]
print("-".join(str(n) for n in nums)) # "1-2-3"
# partition — split into exactly 3 parts (before, sep, after)
print("key=value".partition("=")) # ('key', '=', 'value')It reads backwards at first: ",".join(parts), not parts.join(","). The separator string owns the method because join must work on any iterable of strings — so the method lives on str.
Use casefold() for case-insensitive comparison, not lower(). casefold handles edge cases like the German ß that lower() misses. For aligning output in tables, zfill/ljust/rjust/center are cleaner than manual spacing.
Validation and alignment methods
# Validation — all return True/False
print("12345".isdigit()) # True
print("abc123".isalnum()) # True
print("Hello".isalpha()) # True
print(" ".isspace()) # True
print("HELLO".isupper()) # True
print("hello world".islower()) # True
# Alignment / padding
print("5".zfill(3)) # "005" (zero-fill to width 3)
print("hi".ljust(10, ".")) # "hi........"
print("hi".rjust(10, ".")) # "........hi"
print("hi".center(10, "-")) # "----hi----"
# Case-insensitive comparison — use casefold, not lower
print("Straße".casefold() == "strasse".casefold()) # True (handles ß)
# Remove a prefix/suffix (Python 3.9+)
print("test_file.py".removesuffix(".py")) # "test_file"
print("get_name".removeprefix("get_")) # "name"find returns -1 if not found; index raises ValueError. Use find when "not found" is acceptable; use index when absence is a bug you want to catch."xxhelloxx".strip("x") gives "hello", but "xhxexllo".strip("x") gives "hxexllo" — it only removes from the ends, stopping at the first non-matching character."hello".strip("ho") removes any h or o from the ends, giving "ell" — not the substring "ho".✅ Intermediate tab complete
- I can split a string into a list and join a list back into a string
- I know join is called on the separator: ",".join(parts)
- I can convert numbers to strings before joining
- I know removeprefix/removesuffix (3.9+) for clean prefix/suffix removal
Immutability, interning, and PEP 393
What you’ll learn: How CPython stores strings using the smallest unit that fits (PEP 393), string interning, and why building strings with += in a loop is O(n²) while join is O(n).
How to read this tab: Read when optimising text-heavy code or debugging surprising memory usage.
Immutability, interning, and the str C implementation
CPython implements str as a variable-length object holding a sequence of Unicode code points. Since PEP 393 (Python 3.3), CPython uses a flexible internal representation that stores each string using the smallest unit that fits all its characters — 1 byte (Latin-1), 2 bytes (UCS-2), or 4 bytes (UCS-4). A pure-ASCII string uses one byte per character; adding a single emoji forces the whole string to 4 bytes per character.
import sys
# PEP 393 — internal size depends on widest character
print(sys.getsizeof("abc")) # 52 bytes (1 byte/char + header)
print(sys.getsizeof("ab," + "\u00e9")) # wider — 2 bytes/char
print(sys.getsizeof("ab" + "\U0001F600")) # widest — 4 bytes/char
# String interning — identical literals may share one object
a = "hello"
b = "hello"
print(a is b) # True — compile-time interning of identifiers
# But computed strings are not auto-interned
c = "hel" + "lo" # constant-folded → interned
d = "".join(["h","e","l","l","o"]) # computed → NOT interned
print(c is d) # False
import sys as _s
print(_s.intern(d) is c) # True — manual interning forces sharingBecause strings are immutable, building a large string by repeated concatenation in a loop is O(n²) — each += creates a new string and copies all previous characters. The correct pattern is to collect parts in a list and "".join() them once at the end, which is O(n). This is one of the most important performance idioms in Python text processing.
✅ Expert tab complete
- I know why concatenating in a loop with += is O(n²)
- I know to build strings with a list + join for O(n) performance
- I understand PEP 393 flexible string representation at a high level