Learn Python / String Formatting Deep Dive

String Formatting Deep Dive

You've used f-strings since the first lessons to drop values into text. They can do much more: round numbers, line up columns, add thousands separators, show percentages and pad with zeros. All of it uses a small format specification after a colon inside the braces.

The format specifier

Inside an f-string, {value:spec} formats the value according to spec:

price = 1234.5678

print(f"{price}")
print(f"{price:.2f}")     # 2 decimal places
print(f"{price:,.2f}")    # thousands separator too
Output

The pieces of a spec always appear in this order, and each one is optional:

{value:[fill][align][sign][width][,][.precision][type]}

Let's go through them.

Decimal places

.Nf shows a number with exactly N digits after the decimal point, rounding as needed:

import math

print(f"{math.pi:.0f}")
print(f"{math.pi:.2f}")
print(f"{math.pi:.5f}")
print(f"{2:.2f}")          # whole numbers get decimals too
Output

Thousands separators

A comma (or an underscore) groups digits in thousands:

population = 8_045_311_447

print(f"{population:,}")
print(f"{population:_}")
print(f"{1234567.891:,.2f}")
Output

Percentages

The % type multiplies by 100 and adds a percent sign:

ratio = 0.4567

print(f"{ratio:%}")
print(f"{ratio:.1%}")
print(f"{3 / 8:.0%}")
Output

Width and alignment

A number after the colon sets the minimum width. < aligns left, > aligns right, and ^ centres:

print(f"[{'left':<10}]")
print(f"[{'right':>10}]")
print(f"[{'centre':^10}]")
print(f"[{42:>6}]")         # numbers align right by default
print(f"[{'text':10}]")     # strings align left by default
Output

Fill characters

Put a character before the alignment to fill the space with it instead of spaces:

print(f"{'Menu':*^20}")
print(f"{'Total':.<15}{9.5:.>8.2f}")
print(f"{7:0>3}")           # pad with zeros
print(f"{42:05}")           # zero-padding shortcut
Output

Signs

Control whether positive numbers show a sign:

for change in [3.5, -1.25, 0]:
    print(f"{change:+.2f}")   # always show + or -
Output

Other number formats

n = 255

print(f"{n:b}")     # binary
print(f"{n:o}")     # octal
print(f"{n:x}")     # hexadecimal
print(f"{n:X}")
print(f"{n:#x}")    # with 0x prefix
print(f"{1234567:e}")
print(f"{0.000123:.2e}")
Output

Building a neat table

Widths and alignment really pay off when you print data in columns:

items = [("Coffee", 2, 3.5), ("Blueberry muffin", 1, 2.75), ("Juice", 3, 4.0)]

print(f"{'Item':<18}{'Qty':>4}{'Price':>9}")
print("-" * 31)
total = 0
for name, qty, price in items:
    line = qty * price
    total += line
    print(f"{name:<18}{qty:>4}{line:>9.2f}")
print("-" * 31)
print(f"{'Total':<22}{total:>9.2f}")
Output

Expressions and debugging

Any expression works inside the braces, and adding = prints the expression as well as its value, which is great for debugging:

width = 7
height = 3

print(f"{width * height = }")
print(f"{width=}, {height=}")
print(f"{'python'.upper()!r}")   # !r shows the repr, with quotes
Output

Dynamic widths

The spec itself can contain values in braces:

name = "Ada"
for w in [5, 8, 12]:
    print(f"[{name:>{w}}]")

decimals = 3
print(f"{2 / 3:.{decimals}f}")
Output

Other formatting styles

You'll see two older styles in existing code. They use the same format specs:

name = "Ada"
score = 92.456

print("{} scored {:.1f}".format(name, score))
print("{n} scored {s:.1f}".format(n=name, s=score))
print("%s scored %.1f" % (name, score))
print(format(score, ".1f"))
Output

For new code, f-strings are the clearest choice.

Exercises

Exercise 1: Money

Print the salary with a currency sign, thousands separators and 2 decimal places: Salary: $52,750.50.

salary = 52750.5
Output

Exercise 2: Progress bar

Print the progress as a percentage with 1 decimal place, centred in a 20-character field filled with =. It should print =======62.5%========.

done = 25
total = 40
Output

Exercise 3: Invoice numbers

Print each invoice number padded with zeros to 6 digits and prefixed with INV-, such as INV-000042.

invoice_ids = [42, 1337, 7]
Output

Exercise 4: Leaderboard

Print the leaderboard as a table: the position right-aligned in 3 characters, the name left-aligned in 10, and the score right-aligned in 8 with thousands separators.

  1 Leo          18,500
  2 Mia          12,000
  3 Ava           9,900
players = [("Leo", 18500), ("Mia", 12000), ("Ava", 9900)]
Output

Format specifiers at a glance

A quick reference for later. Results are shown with quotes, so you can see the spaces and padding.

Spec What it does Example Result
.2f 2 decimal places f"{3.14159:.2f}" '3.14'
.0f Rounds to a whole number f"{2.718:.0f}" '3'
, Thousands separators f"{1234567:,}" '1,234,567'
,.2f Separators and 2 decimal places f"{1234.5:,.2f}" '1,234.50'
_ Underscores as separators f"{1234567:_}" '1_234_567'
% Percentage f"{0.256:.1%}" '25.6%'
<6 Left-align in 6 characters f"{'hi':<6}" 'hi '
>6 Right-align in 6 characters f"{'hi':>6}" ' hi'
^6 Centre in 6 characters f"{'hi':^6}" ' hi '
*^6 Centre in 6 characters, filling with * f"{'hi':*^6}" '**hi**'
05 Pad a number with zeros to 5 characters f"{42:05}" '00042'
+ Always show the sign f"{3.5:+.1f}" '+3.5'
b, o, x Binary, octal, hexadecimal f"{255:x}" 'ff'
#x Hexadecimal with the 0x prefix f"{255:#x}" '0xff'
.2e Scientific notation f"{1234567:.2e}" '1.23e+06'
= Shows the expression and its value f"{2 + 3 = }" '2 + 3 = 5'
!r Shows the repr (strings get quotes) f"{'hi'!r}" "'hi'"
{w} inside a spec Takes the width from a variable f"{'hi':>{4}}" ' hi'

Summary

  • {value:spec} formats a value inside an f-string.
  • .2f sets decimal places, , adds thousands separators, and % shows a percentage.
  • A width plus <, > or ^ aligns text, and a fill character goes before the alignment.
  • 05 pads with zeros, and + always shows the sign.
  • {expr=} prints the expression and its value, which is handy for debugging.
  • str.format() and % are older styles with the same specs.