Strings & f-strings
Python str objects represent Unicode text. f-strings embed expressions directly in literals, making formatting readable and fast. Knowing when to use bytes prevents encoding bugs at system boundaries.
Recipe
name = "Ada"
score = 97.456
line = f"{name:>10} | {score:.1f}%"
multiline = f"""Report for {name}
Score: {score:.2f}"""
raw_path = r"C:\new\data\file.txt"When to reach for this:
- Building log messages and user-facing output
- Parsing delimited text (CSV-like) with split/strip
- Encoding text for network or file I/O
- Normalizing user input before validation
Working Example
from pathlib import Path
def build_report(rows: list[dict[str, object]]) -> str:
header = f"{'name':<12} {'role':<10} {'score':>6}"
lines
What this demonstrates:
- f-string format specs align columns (
:<12,:>6.1f) strip,lower, andreplaceform a simple slug pipelinePath.read_text(encoding="utf-8")avoids platform-default encoding surprisesstr.encodeproducesbytesfor wire protocols
Deep Dive
How It Works
- Immutable sequences - Strings cannot change in place; methods return new strings.
- Unicode code points - Python 3
stris Unicode; indexing returns one-code-point strings (mostly). - f-string evaluation - Expressions run at runtime; debug
f"{x=}"shows name and value (3.8+). - bytes vs str -
bytesholds 0-255 values; decode with an explicit codec. - Normalization -
unicodedata.normalizehelps compare visually similar characters.
Common Methods
| Method | Purpose |
|---|---|
split(sep) | Break on delimiter |
join(iterable) | Concatenate with separator |
strip(chars) | Trim ends |
replace(old, new) | Substitute substrings |
removeprefix/prefix | Trim known prefix (3.9+) |
Python Notes
text = " hello "
text.strip().casefold() == "HELLO".casefold() # case-insensitive compare
b"\xc3\xa9".decode("utf-8") # bytes -> str
"résumé".encode("ascii", errors="replace") # lossy fallbackGotchas
- Implicit encoding -
open("f.txt")uses locale default on some systems. Fix: Always passencoding="utf-8". - Concatenating bytes and str -
b"hi" + "there"raises TypeError. Fix: Encode or decode explicitly first. - Mutable default string building - Huge
+=in loops is slow for massive text. Fix: Collect in a list and"".join(lines). - f-string quoting - Nested quotes can break parsing. Fix: Use different quote styles or variables.
- Comparing Unicode visually -
"café" != "café"with different combining characters. Fix: Normalize before compare.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| f-strings | Most formatting | Need lazy template for i18n catalogs |
str.format | Positional templates from config | Simple inline formatting |
Template | Safer substitution from user input | Full format spec needs |
regex (re) | Complex pattern extraction | Simple split suffices |
FAQs
Are f-strings faster than format()?
Generally yes - expressions are evaluated once at runtime with less overhead. Prefer f-strings for new code.
When do I need bytes instead of str?
Network protocols, binary file formats, and crypto digests operate on bytes. Convert at the boundary.
What encoding should I default to?
UTF-8 for text files, HTTP JSON, and logs unless a spec mandates otherwise.
How do I do an f-string multiline?
Use triple quotes: f"""line1\n{name}""". Newlines in the literal are preserved.
What is a raw string?
r"\\server\share" treats backslashes literally - handy for regex and Windows paths in docs.
How do I format thousands separators?
f"{1_000_000:,}" produces 1,000,000. Underscores in literals are cosmetic for readability too.
Can f-strings call functions?
Yes: f"{len(items)} items". Keep expressions simple for readability.
How do I strip punctuation?
str.strip removes only ends. For all occurrences use replace or re.sub.
What does casefold do?
Aggressive case normalization for comparisons - better than lower() for Unicode matching.
How do I safely build SQL strings?
Never interpolate user input. Use parameterized queries from your DB driver or ORM.
Related
- Files & I/O - reading and writing encoded text
- Numbers, Booleans & Arithmetic - numeric format specs
- Comprehensions & Generator Expressions - string joins from iterables
- arrays, bytes & memoryview - binary buffers
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.