Skip to main content
Python beginner Lesson 11 of 28

Python Strings

String methods, f-strings, formatting, slicing, and regex basics — everything you need to work with text in Python.

String Literals

Python supports several string literal forms, each solving a different problem. Choosing the right literal type prevents a whole class of escaping bugs before they start.

single   = 'Hello'
double   = "World"
triple   = """Multi
line"""                     # spans multiple lines without \n
raw      = r"C:\Users\alice" # backslashes are literal — no escape processing
bytes_   = b"binary data"   # bytes literal, not a str
f_string = f"Hello, {name}" # evaluated at runtime — most common for formatting

Indexing and Slicing

Strings are sequences — zero-indexed, supporting the same slice syntax as lists and tuples. Negative indices count from the end, and a step of -1 reverses the string.

s = "Python"

s[0]      # "P"      — first character
s[-1]     # "n"      — last character (equivalent to s[5])
s[1:4]    # "yth"    — characters at index 1, 2, 3
s[:3]     # "Pyt"    — everything up to but not including index 3
s[3:]     # "hon"    — everything from index 3 to the end
s[::2]    # "Pto"    — every second character (step of 2)
s[::-1]   # "nohtyP" — reversed (step of -1)

Common String Methods

String methods are one of Python’s strongest areas for text processing. Every method returns a new string — strings are immutable, so nothing here modifies text in place.

text = "  Hello, World!  "

# Whitespace handling
text.strip()          # "Hello, World!"   — removes both ends
text.lstrip()         # "Hello, World!  " — removes left end only
text.rstrip()         # "  Hello, World!" — removes right end only

# Case conversion
text.lower()          # "  hello, world!  "
text.upper()          # "  HELLO, WORLD!  "
text.title()          # "  Hello, World!  " — capitalizes each word
"hello world".capitalize()  # "Hello world" — capitalizes first char only

# Search and test — useful for input validation and parsing
"Python".startswith("Py")    # True
"Python".endswith("on")      # True
"hello world".find("world")  # 6  — returns -1 if not found
"hello world".index("world") # 6  — raises ValueError if not found
"aababc".count("a")          # 3  — count non-overlapping occurrences

# Replace and split
"hello world".replace("world", "Python")  # "hello Python"
"a,b,,c".split(",")                       # ["a", "b", "", "c"]
"a,b,,c".split(",", maxsplit=2)           # ["a", "b", ",c"]
"  words  with  spaces  ".split()         # ["words", "with", "spaces"] — splits on any whitespace

# Join — the inverse of split; much faster than + concatenation in a loop
", ".join(["Alice", "Bob", "Carol"])      # "Alice, Bob, Carol"
"".join(["P", "y", "t", "h", "o", "n"])  # "Python"

# Character type tests — useful for validating user input
"abc123".isalnum()   # True  — all alphanumeric
"abc".isalpha()      # True  — all alphabetic
"123".isdigit()      # True  — all digits
"  ".isspace()       # True  — all whitespace

f-Strings (Formatted String Literals)

f-strings are Python’s preferred string formatting mechanism. They’re faster than % formatting and str.format(), they support any Python expression inside {}, and they read naturally alongside the surrounding code.

name = "Alice"
age = 30
balance = 1234.567

# Basic variable interpolation
f"Hello, {name}!"

# Any Python expression works inside {}
f"Next year you'll be {age + 1}"

# Format specifiers control number formatting, alignment, and precision
f"Balance: ${balance:.2f}"       # "Balance: $1234.57"    — 2 decimal places
f"Percentage: {0.1234:.1%}"      # "Percentage: 12.3%"   — percent format
f"Padded: {name:>10}"            # "     Alice"           — right-align, width 10
f"Padded: {name:<10}"            # "Alice     "           — left-align
f"Padded: {name:^10}"            # "  Alice   "           — center
f"Zero-padded: {42:05d}"         # "00042"                — zero-fill to width 5
f"Hex: {255:#x}"                 # "0xff"                 — hex with prefix
f"Sci: {1234567.89:.2e}"         # "1.23e+06"             — scientific notation

# Self-documenting expressions (Python 3.8+) — invaluable for debugging
value = 42
f"{value=}"   # "value=42" — prints both the expression and its result

# Multiline f-strings — parentheses allow line continuation
message = (
    f"Name: {name}\n"
    f"Age: {age}\n"
    f"Balance: ${balance:.2f}"
)

str.format()

str.format() is useful when the template string is dynamic — loaded from a config file, database, or user input — since you can’t use f-strings with runtime-determined templates.

template = "Hello, {name}! You have {count} messages."
template.format(name="Alice", count=5)
# "Hello, Alice! You have 5 messages."

# Positional placeholders
"{0} + {1} = {2}".format(1, 2, 3)      # "1 + 2 = 3"

# Reuse a positional argument multiple times
"{0} {0} {1}".format("echo", "end")    # "echo echo end"

Building Strings Efficiently

Because strings are immutable, every + concatenation creates a new string object. In a loop, this produces O(n²) allocations — slow for large data. The fix is to collect parts in a list and join once at the end.

# Slow — a new string object is allocated on every iteration
result = ""
for word in words:
    result += word + " "

# Fast — one allocation at the end
result = " ".join(words)

# Also good — collect parts, join once
parts = []
for item in items:
    parts.append(str(item))
output = ", ".join(parts)

String Encoding

Python 3 strings are Unicode by default — str holds text, bytes holds raw binary data. Encoding converts text to bytes for I/O (files, network sockets), and decoding converts bytes back to text. Always specify utf-8 explicitly rather than relying on the platform default.

# Encode text to bytes for writing to a file or sending over a socket
s = "Hello, 世界"
b = s.encode("utf-8")    # b'Hello, \xe4\xb8\x96\xe7\x95\x8c'

# Decode bytes back to a string after reading from I/O
b.decode("utf-8")         # "Hello, 世界"

import sys
sys.getdefaultencoding()  # "utf-8"

Regex Basics with re

Regular expressions let you search, extract, and transform text based on patterns rather than exact strings. They’re the right tool when str.find() or str.replace() isn’t expressive enough — parsing emails, phone numbers, log lines, or structured text.

import re

text = "Contact us at [email protected] or [email protected]"

# Search for the first match anywhere in the string
match = re.search(r"\b[\w.]+@[\w.]+\.\w+\b", text)
if match:
    print(match.group())   # "[email protected]"

# Find all non-overlapping matches
emails = re.findall(r"\b[\w.]+@[\w.]+\.\w+\b", text)
# ["[email protected]", "[email protected]"]

# Substitute matches with a replacement string
clean = re.sub(r"\d+", "NUM", "I have 3 cats and 12 dogs")
# "I have NUM cats and NUM dogs"

# Split on a pattern rather than a fixed string
parts = re.split(r"[,\s]+", "one, two,  three")
# ["one", "two", "three"]

# Compile a pattern when using it multiple times — avoids recompiling each call
email_re = re.compile(r"\b[\w.]+@[\w.]+\.\w+\b")
email_re.findall(text)

Common Regex Patterns

# Phone number (international format)
re.match(r"^\+?1?\d{9,15}$", "+14155552671")

# URL
re.match(r"https?://[^\s]+", "https://example.com/path")

# ISO date
re.match(r"^\d{4}-\d{2}-\d{2}$", "2024-01-15")

# Named groups — access captured substrings by name, not position
m = re.match(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})", "2024-01-15")
m.group("year")   # "2024"
m.group("month")  # "01"

textwrap

The textwrap module handles two common formatting tasks: wrapping long strings to a fixed column width, and removing consistent leading indentation from multi-line strings (useful when writing multi-line strings inside indented code blocks).

import textwrap

long_text = "This is a very long line that needs to be wrapped at a reasonable column width."

# Wrap to 40 characters per line
print(textwrap.fill(long_text, width=40))

# Remove consistent leading whitespace — useful for embedded SQL or HTML
query = textwrap.dedent("""
    SELECT *
    FROM users
    WHERE active = true
""").strip()

Frequently Asked Questions

Are Python strings mutable?
No. Strings are immutable — every method returns a new string. If you're building a string in a loop, use a list and join() at the end for O(n) performance.
What's the best way to format strings in Python?
f-strings (Python 3.6+) are the most readable and fastest option for most cases. Use str.format() when the template is dynamic or comes from user input.
When should I use raw strings?
Use raw strings (r'...') for Windows paths and regular expression patterns to avoid unintended escape sequence interpretation.