String formatting
You have used f-strings since lesson 2, and :.2f since lesson 3. That colon opens a small language of its own: the format specifier. With it you choose decimals, separators, widths, alignment and more, without changing the value itself. This lesson goes through it piece by piece and ends with a neat table.
The format specifier
Inside the curly braces, the value comes first and the specifier comes after a colon: {value:spec}. The specifier only changes how the value is shown.
1price = 1234567.8912print(f"{price}")3print(f"{price:.2f}")4print(f"{price:.0f}")5print(f"{price:,.2f}")6print(f"{price:_.2f}")
1234567.891 1234567.89 1234568 1,234,567.89 1_234_567.89
.2f means "fixed point, two decimals". A comma before it adds a thousands separator; an underscore does the same with _. The separators also work on whole numbers: f"{5000000:,}" gives 5,000,000.
A % at the end turns a fraction into a percentage: it multiplies by 100, adds the sign, and takes the decimals you ask for.
1done = 72total = 83print(f"{done / total:%}")4print(f"{done / total:.1%}")5print(f"{done / total:.0%}")
87.500000% 87.5% 88%
What does this print?
1print(f"{2 / 3:.0%}") % multiplies by 100 first, so 2 / 3 becomes 66.66..., then .0 rounds it to no decimals: 67%.
Width, alignment and fill
A number in the specifier is a width: the smallest number of characters to use. Shorter values get padded with spaces. The vertical bars below show where each value starts and ends.
1print(f"|{'oil':8}|")2print(f"|{42:8}|")3print(f"|{'oil':>8}|")4print(f"|{'oil':^8}|")5print(f"|{42:<8}|")6print(f"|{'lighthouse':4}|")
|oil | | 42| | oil| | oil | |42 | |lighthouse|
Text is left-aligned by default, numbers right-aligned. <, > and ^ choose left, right and center. The width is only a minimum: 'lighthouse' is longer than 4, so it is shown whole.
Put any character in front of the alignment sign and it is used instead of spaces. That is the fill character.
1print(f"{'Stock':*^15}")2print(f"{'oil':.<12}340")3print(f"{7:0>3}")4print(f"{7:03}")5print(f"{-7:05}")
*****Stock***** oil.........340 007 007 -0007
0>3 pads with zeros on the left. For numbers there is a shortcut: a 0 right before the width, as in 03, pads with zeros and keeps a minus sign in front.
Signs and other bases
By default only negative numbers get a sign. + shows a sign for both; a space leaves room for one, so positive and negative numbers line up.
1print(f"{5:+} {-5:+}")2print(f"{5: } {-5: }")3print(f"{255:b} {255:o} {255:x} {255:X}")4print(f"{255:#x} {255:#b}")5print(f"{5:08b}")6print(f"{1234567:.2e}")
+5 -5 5 -5 11111111 377 ff FF 0xff 0b11111111 00000101 1.23e+06
| Letter | Shows the number as | 255 becomes |
|---|---|---|
b | binary (base 2) | 11111111 |
o | octal (base 8) | 377 |
x / X | hexadecimal (base 16) | ff / FF |
e | scientific notation | 2.550000e+02 |
# adds the prefix Python uses in code (0x, 0b, 0o). The parts combine in a fixed order: fill and alignment, sign, #, 0, width, separator, .decimals, letter. 08b is "zero-pad to width 8, in binary".
A neat table
Give every column the same width and alignment in the header and in each row, and the columns line up. Text columns usually go left, number columns right.
1stock = [("lamps", 12, 1.5), ("oil", 340, 0.25), ("wicks", 5, 12.0)]2print(f"{'Item':<8}{'Qty':>6}{'Price':>8}")3print("-" * 22)4for name, qty, price in stock:5 print(f"{name:<8}{qty:>6}{price:>8.2f}")
Item Qty Price ---------------------- lamps 12 1.50 oil 340 0.25 wicks 5 12.00
The header has no decimals, but uses the same widths: 8, 6 and 8. Each row adds .2f to the price so the decimal points line up too. Press Step to watch each row being built.
Tip: Width counts characters. Chinese, Japanese and Korean characters usually take two columns on screen, so a table with them may not line up even when the code is right.
Debugging with =
Add = after the expression and the f-string shows the expression's text, an equals sign, and its value. It is the quickest way to check what a variable holds.
1lamps = 122name = "Wick"3share = 2 / 34print(f"{lamps=}")5print(f"{name=}")6print(f"{lamps * 3 = }")7print(f"{share=:.2f}")
lamps=12 name='Wick' lamps * 3 = 36 share=0.67
Strings come out with quotes, so you can spot a stray space. Spaces around the = are kept in the output, and a specifier still goes after the colon.
Widths from variables
The width or the number of decimals can come from a variable: put it in its own curly braces inside the specifier.
1names = ["oil", "lamps", "lighthouse"]2w = max([len(n) for n in names])3for n in names:4 print(f"{n:>{w}}|")5digits = 36print(f"{3.14159:.{digits}f}")
oil|
lamps|
lighthouse|
3.142
Here the widest name decides the width, so the table fits whatever names you put in the list.
The older styles
You will meet two older ways in other people's code. format() and str.format use the same specifiers as f-strings; % has its own, similar codes like %.2f and %5d.
1print(format(1234.5, ",.2f"))2print("{} has {} lamps".format("Wick", 12))3print("{0}, {1}, {0}".format("on", "off"))4print("{name:>6}|".format(name="oil"))5print("%s has %d lamps" % ("Wick", 12))6print("%.2f and %5d|" % (3.14159, 42))
1,234.50 Wick has 12 lamps on, off, on oil| Wick has 12 lamps 3.14 and 42|
| Style | Example |
|---|---|
| f-string (use this) | f"{x:.2f}" |
format() function | format(x, ".2f") |
str.format | "{:.2f}".format(x) |
% operator | "%.2f" % x |
format(value, spec) formats one value. str.format fills each {} in order, or by number or name. The % style is the oldest: %s for any value, %d for whole numbers, %.2f for decimals, and a tuple when there is more than one value. For new code, f-strings are the shortest and clearest.
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
A big total
✓Show the total with a thousands separator and two decimals.
Total: $1,234,567.89
1total = 1234567.8912print(f"Total: ${total}")
Progress
✓Print Done: followed by how much of the work is done, as a percentage with one decimal.
Done: 87.5%
1done = 72total = 83print(done, total)
A starred heading
✓Center title in a line of stars. Take the width from width, so changing it changes the line.
***Stock***
1title = "Stock"2width = 113print(title)
What you learned
{value:spec}changes only how a value is shown..2fsets decimals,,or_adds thousands separators,%shows a percentage.- A number sets the width;
<>^align; a character before them is the fill;03pads with zeros. +or a space controls signs;b,o,x,eshow other bases and scientific notation.- Same widths in the header and rows make a table line up.
f"{x=}"shows the expression and its value, handy for debugging.{n:>{w}}takes the width from a variable.format()andstr.formatare older styles with the same specifiers;%uses similar codes like%.2f.