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.

1. Read

Understand the idea in plain English first.

2. Run

Load any example into the compiler and press Run.

3. Change

Edit one value, rerun, and learn from the output.

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.

Hands-on practice

Compiler on the left. Examples on the right.

On desktop, keep the compiler beside the examples. On mobile, the same blocks stack cleanly. Pick an example, try it in the compiler, then change one small thing.

Normalise and report on documents

Live Python compiler

Try it — in-browser Python

Add a document with mixed case and extra spaces, then check the summary table.

Code editor

Output

Python runs in your browser. First run downloads the runtime.

Press Run (or Ctrl+Enter) to execute.

Runs CPython in your browser. NumPy, pandas, scikit-learn and Matplotlib load on demand. Charts appear below the output. No input(), no GPU, no network installs.

Clear code examples

Every example is copy-ready. Use Try in compiler when you want to experiment without scrolling around.

Example

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())

Example

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]))

Example

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}")

Example

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+")

Example

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)

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.