skip to content

In Python's format mini-language, what does the spec in f"{total:>12,.2f}" mean?

level: middleimportance: should knowfreq 55%

answer

  1. Everything after the colon is one grammar
  2. Fill, align, sign, width, grouping, precision, type
  3. The order is fixed and not rearrangeable
  4. Width is a floor, never a ceiling
  5. The type decides which specs are legal

basics

~20 s

Everything after the colon is the format spec: right-align in a field 12 characters wide, group thousands with commas, show two digits after the decimal point, and render as fixed-point. It formats -1234.5678 as " -1,234.57".

solid answer

~50 s

The colon splits a replacement field into the value and its **format spec**, and the spec has a fixed order: `[[fill]align][sign][z][#][0][width][grouping][.precision][type]`. Here `>` is right alignment with the default space fill, `12` is the minimum field width, `,` is the thousands separator, `.2` is precision and `f` selects fixed-point notation. Width is a minimum, never a truncation — a longer value simply overflows the field. Defaults matter: with no alignment, numbers right-align and everything else left-aligns, and a leading `0` means zero-padding after the sign. The same spec language is shared by f-strings and `str.format`, because both hand the text after the colon to the value's `__format__`, so the legal specs depend on the type — `%` and `,` are numeric, `%Y-%m-%d` is what a date accepts. Nesting is allowed: `f"{total:>{width}.2f}"` computes the width at runtime.

code

python · 7 lines
python
total = -1234.5678
print(f"[{total:>12,.2f}]")   # [   -1,234.57]
print(f"[{total:<12,.2f}]")   # [-1,234.57   ]
print(f"[{total:^12.1f}]")    # [  -1234.6   ]
print(f"[{total:08.1f}]")     # [-01234.6]
print(f"[{255:#06x}]")        # [0x00ff]
print(f"[{0.07123:.1%}]")     # [7.1%]

go deeper

for a junior

Know that the colon introduces a format spec and be able to produce two-decimal money and a fixed-width column. .2f, , and a width will cover most of what you write day to day.

for a middle

Recite the order of the spec and explain each part, including that width is a minimum and that alignment defaults differ between numbers and strings. Know that a nested {width} is legal inside a spec.

for a senior

Explain that the spec is delegated to __format__, so what is legal depends on the type — and use that to diagnose a ValueError from an unexpected spec or a conversion applied before it. Know where locale-aware n helps and where it hurts a machine-read file.

for a principal

Own the consistency question: report and export formatting drifts across a codebase until one helper or one set of __format__ implementations owns it. Decide where presentation belongs relative to serialisation, and keep locale-sensitive output out of files another program parses.

### The grammar Inside `{...}`, a colon separates the value from its **format spec**. The spec is a small, fixed-order mini-language: ``` [[fill]align][sign][z][#][0][width][grouping][.precision][type] ``` Every part is optional, but the order is not negotiable — `.2f>12` is not a rearranged `>12.2f`, it is an error. Reading `>12,.2f` left to right gives: align right (`>`) with the implicit space fill, minimum width 12, `,` as the thousands separator, two digits of precision, fixed-point presentation. ### Alignment and fill The alignment characters are `<` (left), `>` (right), `^` (centre) and `=`, which puts the padding *between the sign and the digits* and is only meaningful for numbers. Any single character placed before the alignment character becomes the fill: `f"{name:.<20}"` produces a dotted leader. The defaults are the part people get wrong. With a width but no explicit alignment, **numeric types right-align and everything else left-aligns**, which is why a column of mixed `str` and `int` values built from `{:10}` comes out ragged. A leading `0` before the width (`{:08.2f}`) is shorthand for zero-fill with `=` alignment, so the sign stays at the far left. **Width is a minimum, not a maximum.** A value wider than the field is printed in full and the row is pushed out of shape; if you need truncation you must use `.precision` on a string (`{:.10}` keeps ten characters) or slice the value yourself. ### Sign, grouping and precision `+` shows a sign on positive numbers too, `-` (the default) only on negatives, and a space puts a blank where the sign would be — useful for keeping columns aligned. `z` (added in 3.11) coerces a negative zero to positive zero, so `-0.001` at one decimal prints `0.0` rather than `-0.0`. Grouping is `,` for the conventional thousands separator or `_` for an underscore; with `b`, `o` and `x` the underscore groups four digits at a time, which is how you make a bitmask readable. `.precision` means different things per type: digits after the decimal point for `f` and `%`, total significant digits for `g` and `e`, and a maximum character count for `s`. ### The type character `d` for decimal integers, `f` for fixed-point, `e` for scientific, `g` for the general form that switches between them, `%` to multiply by 100 and append a percent sign, `b`/`o`/`x`/`X` for binary, octal and hex, `n` for a locale-aware number, and `s` for strings. `#` adds the base prefix, so `f"{255:#06x}"` gives `0x00ff` — note that the `0x` counts toward the width. ### The spec is not universal — it belongs to the type This is the mechanism worth stating in an interview: both f-strings and `str.format` take the text after the colon and hand it, unparsed, to the value's `__format__`. `object.__format__` accepts only an empty spec; the numeric and string types implement the mini-language above; a `datetime.date` interprets the whole spec as a strftime pattern, which is why `f"{today:%Y-%m-%d}"` works and looks nothing like the grammar. A class of your own joins in by defining `__format__`, and the spec text it receives is whatever you choose to support. That also explains a common error: `f"{value!r:.2f}"` raises `ValueError`, because `!r` converts the value to a `str` first and `.2f` is not a valid string spec. ### Nesting A replacement field may appear *inside* a spec, one level deep: `f"{total:>{width}.{places}f}"` computes the width and precision at runtime. This is how you align a report whose column widths are only known once the data is loaded, and it works in `str.format` too. ### Practical shape For money, `,.2f` with an explicit width is the workhorse; for percentages, `.1%` beats multiplying by 100 by hand; for keys and identifiers, `{:<24}` plus a fill character produces a readable listing. And when a column of numbers must line up, set the alignment explicitly rather than relying on the per-type default.

  • Why does f"{today:%Y-%m-%d}" work when %Y is not part of the mini-language at all?
    Because the interpreter never parses the spec itself. It passes the text after the colon straight to the value's `__format__`, and `datetime.date.__format__` interprets its argument as a strftime pattern. The mini-language is simply what the numeric and string types happen to implement, so the legal spec is always a property of the type you are formatting.
  • How do you truncate a long value to fit a fixed-width column?
    Width alone will not do it — it is a minimum, so an over-long value overflows the field. For strings, `.precision` truncates: `f"{name:<20.20}"` both pads short names and clips long ones to twenty characters. For anything else, shorten the value before formatting, or format it and slice the result.
  • What does a leading zero mean in a spec like {:08.2f}?
    It is shorthand for zero-fill combined with `=` alignment, which places the padding between the sign and the digits. So `-3.5` at `08.2f` becomes `-0003.50`, keeping the minus sign at the left edge. Add an explicit alignment and the meaning changes: in `>08.2f` the `0` is read as the fill character for ordinary right alignment, and the result is `000-3.50` with the sign buried in the padding.

saying these in an interview costs you the question

  • Thinking width truncates values that are too long
  • Assuming every type accepts the same spec
  • Believing the spec parts can appear in any order
  • Expecting strings to right-align by default under a width
  • Confusing .precision on f with significant digits on g
  • Claiming a format spec cannot contain a nested field

context