f-strings are the modern way to build text: any expression inside {}, plus a compact format spec after : that controls width, alignment, precision, signs and number bases.
f-string basics
name, items, price = "Asha", 3, 49.5
print(f"{name} bought {items} items") # Asha bought 3 items
print(f"total = {items * price}") # total = 148.5
print(f"{name.upper()} {len(name)} {'yes' if items > 2 else 'no'}") # ASHA 4 yes
print(f"{{literal braces}} {items}") # {literal braces} 3
data = {"k": [10, 20]}
print(f"{data['k'][1]}") # 20| Part | Syntax | Example |
|---|---|---|
| Expression | {expr} | f'{a + b}' |
| Conversion | {expr!r} / !s / !a | repr(), str(), ascii() of the value |
| Format spec | {expr:spec} | f'{pi:.3f}' |
| Self-documenting | {expr=} | f'{x=}' → x=5 (3.8+) |
| Nested spec | {expr:{width}.{prec}f} | Width/precision from variables |
| Literal brace | {{ and }} | f'{{x}}' → {x} |
Debugging with = and conversions
x, word = 42, "café"
print(f"{x=}") # x=42
print(f"{x * 2 = }") # x * 2 = 84
print(f"{word=}") # word='café' ← = uses repr() by default
print(f"{word!s}", f"{word!r}", f"{word!a}") # café 'café' 'caf\xe9'
print(f"{x = :>6}|") # x = 42|The format-spec mini-language
[[fill]align][sign][z][#][0][width][grouping][.precision][type]
| Field | Options | Meaning |
|---|---|---|
| fill | any character | Pad character (needs an align after it) |
| align | < > ^ = | Left / right / centre / pad after the sign |
| sign | + - space | Always show sign / only negatives (default) / space for positives |
# | Alternate form: 0b, 0o, 0x prefixes | |
0 | Zero-pad numbers (sign-aware) | |
| width | integer | Minimum field width |
| grouping | , or _ | Thousands separator |
| .precision | integer | Digits after the point (f, %), significant digits (g), or max chars for strings |
| type | d b o x X e f g % n s c | Presentation: decimal, binary, octal, hex, exponent, fixed, general, percent… |
Alignment & padding
s = "py"
print(f"[{s:<6}] [{s:>6}] [{s:^6}]") # [py ] [ py] [ py ]
print(f"[{s:*^8}] [{s:->5}]") # [***py***] [---py]
print(f"[{42:5}] [{'ab':5}]") # [ 42] [ab ] ← numbers right, strings left by default
print(f"[{-42:=+7}] [{-42:07}]") # [- 42] [-000042]
print(f"[{'truncate me':.5}]") # [trunc]Numbers: precision, signs, grouping
pi, big, ratio = 3.14159265, 1234567.891, 0.4567
print(f"{pi:.2f} {pi:.4f} {pi:8.3f}|") # 3.14 3.1416 3.142|
print(f"{big:,.2f} {big:_.1f}") # 1,234,567.89 1_234_567.9
print(f"{ratio:.1%} {ratio:%}") # 45.7% 45.670000%
print(f"{big:e} {big:.2e}") # 1.234568e+06 1.23e+06
print(f"{pi:g} {big:g} {0.00001234:g}") # 3.14159 1.23457e+06 1.234e-05
print(f"{5:+d} {-5:+d} {5: d}") # +5 -5 5
print(f"{1_000_000:,}") # 1,000,000Integers in other bases
n = 255
print(f"{n:b} {n:o} {n:x} {n:X}") # 11111111 377 ff FF
print(f"{n:#b} {n:#o} {n:#x}") # 0b11111111 0o377 0xff
print(f"{5:08b}") # 00000101
print(f"{n:#06x}") # 0x00ff ← width counts the prefix
print(f"{65:c}") # A
print(format(n, "x"), int("ff", 16)) # ff 255Nested & dynamic specs
rows = [("apple", 1.5), ("kiwi", 12.25)]
w = max(len(name) for name, _ in rows)
for name, cost in rows:
print(f"{name:<{w}} | {cost:>{8}.{2}f}")apple | 1.50
kiwi | 12.25Dates in f-strings
from datetime import datetime
d = datetime(2026, 9, 29, 14, 5)
print(f"{d:%Y-%m-%d %H:%M}") # 2026-09-29 14:05
print(f"{d:%d %b %Y, %A}") # 29 Sep 2026, Tuesday
print(f"{d:%I:%M %p}") # 02:05 PM__format__ for your own classes
class Money:
def __init__(self, amount):
self.amount = amount
def __format__(self, spec):
if spec == "inr":
return f"₹{self.amount:,.2f}"
return format(self.amount, spec) # fall back to float formatting
m = Money(1234.5)
print(f"{m:inr}") # ₹1,234.50
print(f"{m:>10.1f}|") # 1234.5|
print(format(m, "inr")) # ₹1,234.50Older styles: str.format, % and Template
print("{} + {} = {}".format(1, 2, 3)) # 1 + 2 = 3
print("{0}{1}{0}".format("ab", "-")) # ab-ab
print("{name} is {age}".format(name="Ravi", age=30)) # Ravi is 30
point = {"x": 3, "y": 4}
print("({p[x]}, {p[y]})".format(p=point)) # (3, 4)
print("{:>6.2f}|".format(3.14159)) # 3.14|
print("%s has %d items (%.1f%%)" % ("cart", 3, 42.456)) # cart has 3 items (42.5%)
print("%(who)s says hi" % {"who": "Asha"}) # Asha says hi
from string import Template
t = Template("Hello $name, you owe $$${amount}")
print(t.substitute(name="Kiran", amount=20)) # Hello Kiran, you owe $20
print(t.safe_substitute(name="Kiran")) # Hello Kiran, you owe $${amount}| Style | Use it for |
|---|---|
| f-strings | Default choice — fastest and most readable (3.6+) |
str.format | Templates stored separately from the values, e.g. FMT.format(**row) |
% formatting | Legacy code and logging calls (log.info('%s', x) formats lazily) |
string.Template | Templates written by users — $name only, no expressions, so it's safe |
Python 3.12+: f-strings grew up
songs = ["a", "b"]
print(f"{", ".join(songs)}") # a, b ← same quotes inside (3.12+)
print(f"{'\n'.join(songs)!r}") # 'a\nb' ← backslashes allowed in expressions
total = f"{
sum([1, 2, 3]) # comments and line breaks inside {} are fine
}"
print(total) # 6Pitfalls
f'{x:.2f}'on a string raisesValueError: Unknown format code 'f'— convert first.round(x, 2)changes the value;f'{x:.2f}'only changes how it is displayed. For money useDecimal.=in{x=}usesrepr, so strings appear quoted — add!sfor the plain form.Forgetting the
fprefix silently prints the braces:'{name}'.
How do you print a float with 2 decimals and thousands separators, right-aligned in 12 characters?
Which string formatting method is fastest?