Nested f-strings in Python
A nested f-string puts an f-string (or a format spec computed at runtime) inside the curly braces of another f-string. It's the cleanest way to make precision, width, or fill characters dynamic — but quote rules and parser limits trip people up.
What Is a Nested f-string?
Two patterns show up under the "nested" label:
- Dynamic format spec — the part after the colon is computed:
f"{value:.{n}f}" - f-string inside an f-string expression — a literal f-string is one of the values:
f"{f'inner {x}'}"
The first is by far the more useful pattern.
Dynamic Precision (the Most Common Use)
value = 3.14159265359
for n in range(1, 5):
print(f"{value:.{n}f}")
# 3.1
# 3.14
# 3.142
# 3.1416
Dynamic Width and Padding
names = ["Alice", "Bob", "Charlie"]
width = max(len(n) for n in names) # 7
for n in names:
print(f"{n:<{width}} | done")
# Alice | done
# Bob | done
# Charlie | done
Dynamic Fill Character
fill = "*"
title = "REPORT"
f"{title:{fill}^20}"
# '*******REPORT*******'
Conditional Formatting Inside an f-string
Use a ternary expression — this isn't truly "nested," but it's the second-most-common reason people search for nested f-strings.
change = -0.042
f"{('▼' if change < 0 else '▲')} {abs(change):.1%}"
# '▼ 4.2%'
# Conditional precision:
n = 0
f"{value:.{2 if n == 0 else n}f}" # '3.14' when n is 0
The Quote Rule (Python 3.11 and Earlier)
Before Python 3.12 you could not reuse the same quote character inside an f-string expression. This forces you to alternate quote styles:
# Python 3.11 — must use the OTHER quote inside braces:
data = {"name": "Alice"}
f"{data['name']}" # OK — single quotes inside double-quoted f-string
# f"{data["name"]}" # SyntaxError on 3.11 and earlier
# To nest f-strings, alternate:
inner = "world"
f"hello {f'{inner}!'}" # 'hello world!'
Python 3.12+ Relaxed the Quote Rule (PEP 701)
# Python 3.12+ — same quotes are fine:
data = {"name": "Alice"}
f"{data["name"]}" # 'Alice' (would error pre-3.12)
# You can also nest f-strings with the same quotes:
f"hello {f"{inner}!"}" # 'hello world!' on 3.12+
# And backslashes are allowed in expressions:
items = ["a", "b"]
f"{'\n'.join(items)}" # OK on 3.12+, SyntaxError before
When to Use a Nested f-string vs format()
If the format spec is fully dynamic and reused, str.format() is often clearer:
spec = ".2f"
value = 3.14159
# Nested f-string:
f"{value:{spec}}" # '3.14'
# format() — same result, sometimes cleaner:
"{:{}}".format(value, spec) # '3.14'
# format_map for many fields:
"{value:{spec}}".format_map({"value": value, "spec": spec})
Common Pitfalls
- Quotes in expressions on Python ≤ 3.11: alternate quote styles, or assign to a variable first.
- No backslashes in expressions on ≤ 3.11:
f"{'\n'}"is a SyntaxError. Usechr(10)or assign the value first. - One level of nesting only: the format-spec nest can itself contain a replacement field (
:.{n}f), but that inner field cannot have its own format spec. - Readability: three-level nests are legal but unreadable. Pull complex logic into a variable.
Pro Tip: Check your Python version with python --version. If you're on 3.12 or newer, the PEP 701 relaxation means most "nested f-string is a SyntaxError" advice on Stack Overflow is outdated.