String Formatting with f-strings
f-strings — string literals prefixed with f — are the modern way to build text from values. Beyond inserting variables, their format specifiers control decimal places, padding, alignment, thousands separators, percentages and dates, all inline.
The basics
name = "Ana" items = 3 price = 4.5 print(f"Hi {name}, you have {items} items.") print(f"Total: {items * price}") print(f"Shout: {name.upper()}!")
Hi Ana, you have 3 items. Total: 13.5 Shout: ANA!
Anything between braces is a Python expression, evaluated and converted to a string. To include a literal brace, double it: f"{{not a field}}".
Format specifiers
After the expression, a colon introduces a format spec:
pi = 3.14159265 big = 1234567.891 ratio = 0.256 print(f"{pi:.2f}") print(f"{big:,.2f}") print(f"{big:_.0f}") print(f"{ratio:.1%}") print(f"{42:05d}") print(f"{255:x} {255:b} {255:#x}") print(f"{1e-7:.2e}")
3.14 1,234,567.89 1_234_568 25.6% 00042 ff 11111111 0xff 1.00e-07
| Spec | Meaning |
|---|---|
.2f | Fixed-point, 2 decimal places |
, or _ | Thousands separator |
% | Multiply by 100 and add a percent sign |
05d | Integer, zero-padded to width 5 |
x b o e | Hex, binary, octal, scientific notation |
Width and alignment
rows = [("Apples", 3, 1.2), ("Bananas", 12, 0.5), ("Cherries", 200, 0.05)] print(f"{'Item':<10}{'Qty':>5}{'Price':>8}") for item, qty, price in rows: print(f"{item:<10}{qty:>5}{price:>8.2f}") print(f"{'centred':^20}|") print(f"{'pad':*^11}")
Item Qty Price
Apples 3 1.20
Bananas 12 0.50
Cherries 200 0.05
centred |
****pad****<, > and ^ align left, right and centre within the given width; a character before them sets the fill. This makes neat text tables easy.
Debugging with =
Adding = after an expression prints the expression itself along with its value — perfect for quick debugging:
width = 12 height = 5 print(f"{width=}, {height=}, {width * height=}") print(f"{width / 7 = :.3f}")
width=12, height=5, width * height=60 width / 7 = 1.714
repr and str conversions
text = "line\nbreak" print(f"{text}") print(f"{text!r}")
line break 'line\nbreak'
!r uses repr(), showing quotes and escape sequences — useful for spotting stray whitespace.
Dates and nested specs
from datetime import date d = date(2026, 10, 6) print(f"{d:%A, %d %B %Y}") print(f"{d:%Y-%m-%d}") decimals = 3 print(f"{2 / 3:.{decimals}f}")
Tuesday, 06 October 2026 2026-10-06 0.667
Objects can define their own format specs; dates use strftime codes (lesson 49). Specs can themselves contain fields, like {decimals} above.
Multi-line f-strings
order = {"id": 1042, "customer": "Rosa", "total": 7.8}
receipt = (
f"Order #{order['id']}\n"
f"Customer: {order['customer']}\n"
f"Total: ${order['total']:.2f}"
)
print(receipt)
Order #1042 Customer: Rosa Total: $7.80
Adjacent string literals are joined automatically, which keeps long f-strings readable. Since Python 3.12, you can even reuse the same quote type inside the braces, e.g. f"{order["id"]}".
Older formatting styles
You'll see two older styles in existing code. They still work, but f-strings are clearer:
name, score = "Ana", 91.5 print("%s scored %.1f" % (name, score)) print("{} scored {:.1f}".format(name, score)) print(f"{name} scored {score:.1f}")
Ana scored 91.5 Ana scored 91.5 Ana scored 91.5
One exception: when logging, pass values as arguments — logging.info("User %s logged in", name) — so the message is only formatted if it's actually logged.