Overview
Use f-strings by default; use str.format when the template is data (a config value or translation); keep % only for logging calls. Python 3.14 adds t-strings for safe interpolation into HTML, SQL, and shell.
Which one
Use f-strings by default and % only for logging.
| Style | Example | Use when |
|---|---|---|
| f-string | f"{name} is {age}" | Template is literal code. |
str.format | "{0} {name}".format(a, name=b) | Template is a runtime string. |
format_map | "{x}".format_map(d) | Values come from one mapping; a __missing__ method can supply defaults. |
% | log.info("x=%s", x) | logging, which formats lazily and only if the record is emitted. |
| t-string (3.14+) | t"Hi {name}" | You need a string.templatelib.Template, not a str, so a library can escape each value. |
str.format, percent, and custom formats
Use these forms when the template is not a literal.
| Form | Example |
|---|---|
| Reuse or unpack | "{0} {0}".format(a), "{x}".format(**d) |
| Attribute or item access | "{0.name}".format(obj), "{0[key]}".format(d) |
| Percent codes | "%s", "%d", "%.2f", "%r", "%(key)s" % d |
| Custom spec | format(obj, "spec") calls obj.__format__("spec"); implement it to embed domain objects in f-strings. |
f-string features
Use = for debug output and nested specs for dynamic widths.
| Pattern | Example | Output |
|---|---|---|
| Self-documenting (3.8+) | f"{value=}" | value=42 |
| Conversion | f"{obj!r}", !s, !a | repr, str, ascii |
| Dynamic width and precision | f"{x:{width}.{prec}f}" | 3.142 |
| Date | f"{dt:%Y-%m-%d %H:%M}" | 2026-05-14 09:30 |
| Nested quotes | f"{d['k']}" | Use a different quote inside; Python 3.12+ lifts this limit. |
Format spec
Grammar after the colon: [[fill]align][sign][z][#][0][width][grouping][.precision][type].
| Spec | Example | Output |
|---|---|---|
.2f | f"{3.14159:.2f}" | 3.14 |
, / _ grouping | f"{1000000:,}" | 1,000,000 / 1_000_000 with _ |
.1% | f"{0.42:.1%}" | 42.0% |
.2e | f"{12345.6:.2e}" | 1.23e+04 |
08d | f"{42:08d}" | 00000042 |
>10, <10, ^10 | f"{'hi':>10}" | Right, left, center in width 10. |
+d | f"{42:+d}" | +42 |
x, #x, b | f"{255:#x}" | 0xff (also b binary, o octal) |
Gotchas
Avoid these; each evaluates too early or too late, or interpolates untrusted input.
| Gotcha | Fix |
|---|---|
| An f-string is evaluated where it is written, not when it is used. | Use str.format or a function for deferred templates. |
Before Python 3.12, backslashes and reused quote types are not allowed inside {}. | Compute the value first, or alternate quote types. |
f"..." in a logging call formats even when the log is dropped. | Pass arguments: log.info("x=%s", x). |
"%s" % value breaks when value is a tuple. | Write "%s" % (value,). |
| Formatting a float rounds for display only. | Call round() if you need the rounded value. |
Spec type n depends on the current locale. | Use , or _ for portable output. |
| Never build SQL, HTML, or shell commands with f-strings from user input. | Use parameterized queries, escaping, or a t-string-aware library. |