← Home

033. .Format() Method

Template strings with placeholder substitution

033. .Format() Method

RAM Manager: Reading Legacy Code

Aryan is adding features to his company’s existing monitoring tool. The codebase is Python 3.4-era — everything uses .format() because f-strings didn’t exist yet. He needs to understand and fix it.

# Common .format() patterns he finds in the codebase
"{name} [{pid}]: {mem}MB".format(name="chrome", pid=812, mem=544)
"PID {0:>6} | {1:<20} | {2:>6}MB".format(812, "chrome", 544)

The broken templates have two bugs: a wrong index ({3} when only 3 args are passed, so index 2 is the last) and a mismatched keyword name ({message} when the kwarg is msg=). Both cause exceptions at runtime — the kind of bug that only surfaces when you actually run the formatter.


💡 Fun fact: .format() was introduced in Python 2.6 (2008) as a replacement for the % operator. F-strings didn’t arrive until Python 3.6 (2016) — so .format() ruled Python string formatting for nearly a decade and is still widely used in legacy codebases.

⚠️ Watch out: A KeyError or IndexError from .format() only fires at runtime, not at import time. A template like "Hello {nmae}" with a typo will sit silently in your code until that line actually executes — unit tests are the only reliable way to catch this early.

🤔 Think about it: If .format() raises a KeyError when a named placeholder has no matching keyword, why does "{} {}".format("a") raise an IndexError instead? What does that tell you about how Python handles positional vs. named placeholders internally?

Learning objectives

  • Use positional placeholders {0}, {1} with .format()
  • Use named placeholders {name} with .format(name=value)
  • Apply format specifiers for alignment and precision

Key concepts

  • format()
  • string templates
  • placeholders

Try it

Concept detail

str.format() replaces {} placeholders with arguments.

Positional: “Hello {0} and {1}”.format(“Alice”, “Bob”) → “Hello Alice and Bob” Auto-numbered: “Hello {} and {}”.format(“Alice”, “Bob”) → same (Python counts for you) Named: “Hello {name}”.format(name=“Alice”) → “Hello Alice” Mixed: “{0} is {age}”.format(“Alice”, age=30) → “Alice is 30”

Format specs: same mini-language as f-strings “{:.2f}”.format(3.14159) → “3.14” “{:>10}”.format(“hi”) → “ hi“ “{:<10}”.format(“hi”) → “hi “

Runtime errors .format() raises: IndexError if a positional index is out of range KeyError if a named placeholder has no matching keyword argument

.format() is pre-3.6 but still used for: reusable template strings stored in config files or databases, and code that needs to run on Python < 3.6.

Solution

def make_invite(name, event, date):
    return "Dear {0}, you're invited to {1} on {2}.".format(name, event, date)

def make_error(code, msg):
    return "Error {code}: {msg}".format(code=code, msg=msg)

def make_table_row(rank, name, score):
    return "{:3}. {:<10} {}".format(rank, name, score)

Tests

def test_invite():
    result = make_invite("Alice", "PyCon", "2024-05-10")
    assert result == "Dear Alice, you're invited to PyCon on 2024-05-10."

def test_invite_no_index_error():
    # {3} raises IndexError — make sure it runs without error
    try:
        result = make_invite("Bob", "DjangoCon", "2024-09-22")
        assert "Bob" in result
    except IndexError:
        assert False, "IndexError raised — wrong index in template"

def test_error():
    result = make_error(404, "Page not found")
    assert result == "Error 404: Page not found"

def test_error_no_key_error():
    try:
        result = make_error(500, "Internal error")
        assert "500" in result
    except KeyError:
        assert False, "KeyError raised — mismatched keyword name"

def test_table_row():
    result = make_table_row(1, "Alice", 100)
    assert result.startswith("  1.")
    assert "Alice" in result
    assert "100" in result

Resources