String methods and formatting
The full string toolkit: case, search, split and join, replace, padding, and f-string format specifiers.
Why this matters in AI / ML / GenAI
Cleaning documents before embedding, normalising labels, parsing model output, and printing readable metrics are all string work. f-string format specifiers are how you produce log lines and reports that people can actually scan.
The method groups
Strings are immutable — every method returns a new string. Assign the result.
Case: lower, upper, title, capitalize, swapcase, casefold (aggressive lowercase for comparisons across languages).
Whitespace: strip, lstrip, rstrip — pass characters to strip something specific, e.g. strip(".,").
Search: find (-1 if absent), index (raises), count, startswith, endswith, and the in operator.
Split and join: split, rsplit, splitlines, partition, and "sep".join(list).
Transform: replace, removeprefix, removesuffix, zfill, ljust, rjust, center.
Tests: isdigit, isalpha, isalnum, isspace, islower, isupper.
Note "".join(parts) is dramatically faster than repeated += in a loop, because each += allocates a whole new string.
Format specifiers
Inside an f-string, everything after : is a format specification.
| Spec | Meaning | Example output |
|---|---|---|
:.2f | 2 decimal places | 0.91 |
:.1% | percentage | 91.3% |
:, | thousands separator | 1,234,567 |
:>10 | right align, width 10 | hello |
:<10 | left align | hello |
:^10 | centre | hello |
:08.3f | zero-pad, 3 decimals | 0004.500 |
:e | scientific | 2.000000e-05 |
:+ | always show sign | +5 |
{value!r} inserts repr(value) instead of str(value) — it shows quotes and escapes, which is what you want in logs and error messages.
{name=} prints both the expression and its value: f"{lr=}" gives lr=2e-05. Excellent for quick debugging.
Multi-line text and escapes
Triple quotes preserve newlines and are the natural home for prompt templates.
textwrap.dedent strips the common leading indentation, so an indented triple-quoted string inside a function does not carry the code's indentation into the prompt.
Escapes: \n newline, \t tab, \\ backslash, \" quote.
Raw strings (r"...") disable escapes and are required for regular expression patterns and Windows paths.
Copy-paste examples
Copy into your own editor, or load one into the compiler below and press Run.
Cleaning a document
The normalisation pipeline you run before embedding text.
raw = " The QUICK brown Fox...\n\n jumped! "
step1 = raw.strip()
step2 = step1.lower()
step3 = " ".join(step2.split()) # collapse all whitespace
step4 = step3.replace("...", ".").strip(".!")
print(repr(raw))
print(repr(step1))
print(repr(step3))
print(repr(step4))
print("words:", len(step4.split()))
print("title case:", step4.title())Search, split, and join
partition is useful for splitting on the first separator only.
line = "2026-09-04 ERROR model=gpt-4.1-mini latency_ms=2450"
print("starts with date:", line.startswith("2026"))
print("contains ERROR :", "ERROR" in line)
print("position of 'model':", line.find("model"))
print("count of '=' :", line.count("="))
parts = line.split()
print("fields:", parts)
fields = {}
for token in parts:
if "=" in token:
key, _, value = token.partition("=")
fields[key] = value
print("parsed:", fields)
print("rejoined:", " | ".join(parts[:3]))Format specifiers in a metrics table
Alignment and precision turn output into a readable report.
rows = [
("gpt-4.1-mini", 0.913, 128, 0.000372),
("llama-3-8b", 0.8471, 1520, 0.0),
("claude-haiku", 0.9002, 460, 0.00124),
]
print(f"{'model':<15}{'accuracy':>10}{'latency':>10}{'cost':>12}")
print("-" * 47)
for name, acc, latency, cost in rows:
print(f"{name:<15}{acc:>9.1%}{latency:>9,d}ms${cost:>11.6f}")
lr = 2e-5
print(f"\ndebug style: {lr=}")
print(f"repr style : {'text with \"quotes\"'!r}")
print(f"sign : {5:+d} {-5:+d}")
print(f"zero pad : {7:03d}")Multi-line prompts with dedent
dedent keeps the prompt clean even when the code is indented.
import textwrap
def build_prompt(question, context):
template = """
You are a precise assistant.
Use only the context below.
Context:
{context}
Question: {question}
"""
return textwrap.dedent(template).strip().format(context=context, question=question)
print(build_prompt("What is RAG?", "RAG retrieves documents, then generates."))
print("---")
print("raw string for regex:", r"\d+\.\d+")Why join beats += in a loop
Each += builds a whole new string; join allocates once.
import time
words = [f"token{i}" for i in range(20_000)]
start = time.perf_counter()
acc = ""
for w in words:
acc += w + " "
concat_ms = (time.perf_counter() - start) * 1000
start = time.perf_counter()
joined = " ".join(words) + " "
join_ms = (time.perf_counter() - start) * 1000
print(f"+= in loop : {concat_ms:7.2f} ms")
print(f"str.join : {join_ms:7.2f} ms")
print("same result:", acc == joined)Normalise and report on documents
Try it — in-browser Python
Add a document with mixed case and extra spaces, then check the summary table.
Output
Python runs in your browser. First run downloads the runtime.
Press Run (or Ctrl+Enter) to execute.
CPython in WebAssembly. Stdlib works. NumPy, pandas, scikit-learn and Matplotlib load on demand, and charts render below. No input(), no GPU, no network installs.
Takeaways
- String methods return new strings — always assign the result.
- Use "".join(list) instead of += inside a loop.
- Format specifiers (:.2f, :.1%, :>10, !r, =) turn raw values into readable reports.