Beginner20 min

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.

SpecMeaningExample output
:.2f2 decimal places0.91
:.1%percentage91.3%
:,thousands separator1,234,567
:>10right align, width 10    hello
:<10left alignhello&nbsp;&nbsp;&nbsp;&nbsp;
:^10centre&nbsp;&nbsp;hello&nbsp;&nbsp;
:08.3fzero-pad, 3 decimals0004.500
:escientific2.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.