---
title: "Python String Formatting Cheatsheet"
slug: "python-string-formatting"
category: "cheatsheets"
tags: ["cheatsheets", "python", "strings", "f-strings", "formatting", "reference"]
status: "stable"
last_updated: 2026-05-14
summary: "Python string formatting methods side by side: f-strings, str.format, format_map, percent-style, with format specifiers and when to use each."
related:
  [
    "[[coding/python]]",
    "[[cheatsheets/python-collections]]",
    "[[cheatsheets/python-itertools]]",
    "[[cheatsheets/python-regex]]",
    "[[cheatsheets/typescript-utility-types]]",
    "[[backend/fastapi]]",
    "[[cheatsheets/index|Cheatsheets]]",
  ]
---

## Overview

Python has four string formatting mechanisms. F-strings (3.6+) are the default choice for new code: they are readable, fast, and evaluated at the call site. Use `str.format` when the template lives in a variable or config file; use `format_map` when the values come from a dict and missing keys should raise clearly. Avoid percent-style (`%`) in new code; it survives in logging calls for lazy evaluation. This card covers syntax, specifiers, and the non-obvious edges.

## F-string syntax

F-strings are string literals prefixed with `f` or `F`. Expressions inside `{}` are evaluated at runtime.

| Pattern | Example | Output |
|---|---|---|
| Simple variable | `f"{name}"` | `"Alice"` |
| Expression | `f"{2 + 2}"` | `"4"` |
| Method call | `f"{name.upper()}"` | `"ALICE"` |
| Attribute | `f"{obj.value}"` | attribute value |
| Dict access | `f"{d['key']}"` | dict value (use different quote inside) |
| Conditional | `f"{'yes' if ok else 'no'}"` | conditional value |
| Self-documenting (3.8+) | `f"{value=}"` | `"value=42"` |
| Nested f-string | `f"{f'{x:.{prec}f}'}"` | dynamic precision |
| Multiline | `f"line1\n{var}"` | newline is literal `\n` not backslash |

Backslash escapes are not allowed inside `{}` in Python < 3.12. Use a variable for complex expressions to stay readable.

## str.format and format_map

Use `str.format` when the template is a runtime string. Use `format_map` to pass a mapping and allow custom `__missing__` behavior.

| Pattern | Example | Notes |
|---|---|---|
| Positional | `"{0} {1}".format(a, b)` | Index order explicit |
| Keyword | `"{x} {y}".format(x=1, y=2)` | Readable, order-independent |
| Reuse positional | `"{0} {0}".format(a)` | Same arg twice |
| Dict unpack | `"{x}".format(**d)` | Unpack a dict |
| `format_map` | `"{x}".format_map(d)` | Avoids copy; supports `__missing__` |
| Attribute access | `"{0.name}".format(obj)` | Dot access inside template |
| Index access | `"{0[key]}".format(d)` | Bracket access inside template |

`format_map` with a `defaultdict` or custom class lets you silently skip missing keys, useful for partial template rendering.

## Format specifiers

The format spec goes after `:` inside `{}`. The grammar is `[[fill]align][sign][z][#][0][width][grouping][.precision][type]`.

| Specifier | Meaning | Example | Output |
|---|---|---|---|
| `d` | Integer decimal | `f"{42:d}"` | `"42"` |
| `f` | Float fixed-point | `f"{3.14159:.2f}"` | `"3.14"` |
| `e` | Scientific notation | `f"{12345.6:.2e}"` | `"1.23e+04"` |
| `g` | General (auto e/f) | `f"{0.00012:.2g}"` | `"0.00012"` |
| `%` | Percentage | `f"{0.42:.1%}"` | `"42.0%"` |
| `s` | String | `f"{'hi':>10s}"` | `"        hi"` |
| `b` | Binary | `f"{10:b}"` | `"1010"` |
| `x` / `X` | Hex lower/upper | `f"{255:x}"` | `"ff"` |
| `o` | Octal | `f"{8:o}"` | `"10"` |
| `n` | Locale-aware number | `f"{1000000:n}"` | locale-dependent |
| `,` | Thousands separator | `f"{1000000:,}"` | `"1,000,000"` |
| `_` | Underscore separator | `f"{1000000:_}"` | `"1_000_000"` |
| `08d` | Zero-pad width 8 | `f"{42:08d}"` | `"00000042"` |
| `>10` | Right-align width 10 | `f"{'hi':>10}"` | `"        hi"` |
| `<10` | Left-align width 10 | `f"{'hi':<10}"` | `"hi        "` |
| `^10` | Center width 10 | `f"{'hi':^10}"` | `"    hi    "` |
| `+d` | Force sign | `f"{42:+d}"` | `"+42"` |
| `#x` | Hex with prefix | `f"{255:#x}"` | `"0xff"` |

Dynamic width and precision: `f"{value:{width}.{prec}f}"` where `width` and `prec` are variables.

## Percent-style formatting

Percent-style predates `str.format`. Retain it only in `logging` calls where lazy evaluation avoids formatting strings that are never emitted.

| Pattern | Example | Notes |
|---|---|---|
| `%s` | `"Hello %s" % name` | Calls `str()` |
| `%d` | `"Value: %d" % n` | Integer |
| `%f` | `"%.2f" % 3.14` | Float fixed |
| `%r` | `"%r" % obj` | Calls `repr()` |
| Named | `"%(key)s" % d` | Dict lookup |
| Logging | `log.info("x=%s", val)` | Lazy; do not use `f"x={val}"` here |

Never use percent-style for user-facing output in new Python 3 code. Use f-strings or `str.format`.

## Datetime and custom `__format__`

`format()` calls `__format__` on any object. Datetime objects accept `strftime`-style codes directly.

| Pattern | Example | Output |
|---|---|---|
| Date | `f"{dt:%Y-%m-%d}"` | `"2026-05-14"` |
| Time | `f"{dt:%H:%M:%S}"` | `"09:30:00"` |
| Custom object | `format(obj, "spec")` | Calls `obj.__format__("spec")` |
| `!r` conversion | `f"{obj!r}"` | `repr(obj)` |
| `!s` conversion | `f"{obj!s}"` | `str(obj)` |
| `!a` conversion | `f"{obj!a}"` | `ascii(obj)` |

Implement `__format__` on domain objects to make them directly embeddable in f-strings with custom specs.

## Common gotchas

- F-strings evaluate at definition time, not call time; wrapping in a `lambda` or function defers evaluation.
- Single-quoted strings inside `{}` conflict with single-quoted f-strings in Python < 3.12. Use `"` outside or `'''` to wrap.
- `format` on a float rounds to the display precision but does not change the stored value. Use `round()` if you need the rounded number.
- `%` with a single argument that is a tuple requires an extra wrap: `"%s" % (value,)`, not `"%s" % value` when `value` is itself a tuple.
- `format_map` does not copy the mapping; mutations to the source dict during formatting are visible.
- Locale-aware specifiers (`n`) depend on `locale.setlocale`. Prefer `,` or `_` for portable code.

## Related

- [[coding/python]]
- [[cheatsheets/python-collections]]
- [[cheatsheets/python-itertools]]
- [[cheatsheets/python-regex]]
- [[backend/fastapi]]
- [[cheatsheets/typescript-utility-types]]
- [[cheatsheets/index|Cheatsheets]]
