Why it matters
A program that prints 3.14159265 next to Tea is correct, but nobody wants to read it. A receipt, a table of scores or a progress line needs its columns to line up and its numbers to show the same number of decimal places. Printing text in a fixed shape is called formatting. Formatting is the difference between output a person can scan and output that only a computer finds easy to read.
Python handles formatting with a format spec. A format spec is a short piece of text after a colon inside braces, such as {3.14159:.2f}. The part before the colon is the value, and the part after the colon says how to show it: the width, the alignment, the number of decimal places and so on. You can write the spec inside an f-string, which is a string with an f before the opening quote, or you can use str.format with the same specs.
This lesson covers f-strings and str.format first, then the older % operator, which you will still see in code written before f-strings existed. It also covers the rules that surprise beginners, such as rounding. Reading text from a user or splitting a line into fields is not covered here. The CSV splitting lesson in this module covers that.
The idea: a template with holes
Think of a form letter. The text "Dear NAME, your balance is AMOUNT." stays the same for every customer. Only the two holes change. An f-string works the same way. Here is the smallest useful example.
The output is:
Dear Ada, your balance is 42.50.The braces {name} put the value in place. The part {balance:.2f} has a spec, .2f, which means a decimal number with exactly two digits after the decimal point. The f at the end of the spec is the type, and it says the value is a float, which is Python's name for a decimal number.
A spec has up to five parts, in this order: a fill character, an alignment (< for left, > for right, ^ for centre), a sign (+ or -), a width such as 10, and a precision such as .3. The type letter comes last. So {3.14159:-^10.3f} means: fill with -, centre the value, give it a width of 10, show three decimal places.
These are the types beginners use most.
| Spec | Meaning | Example output |
|---|---|---|
d | whole number (integer) | 42 for the value 42 |
f | decimal number, 6 digits after the point by default | 3.141590 for 3.14159 |
s | text | Ada |
x | whole number in hexadecimal (base 16) | ff for 255 |
o | whole number in octal (base 8) | 10 for 8 |
b | whole number in binary (base 2) | 101 for 5 |
e | scientific notation (a number times a power of 10) | 1.234568e+04 for 12345.678 |
, | thousands separator, placed before the type | 1,234,567 for 1234567 |
% | a number times 100 with a percent sign | 45.7% for 0.4567 with .1% |
With no type letter, Python picks the natural type of the value. An integer prints as a whole number, a string prints as text, and a float prints in its short form. The type letter forces a specific form, which is useful when you want every row of a table to look the same.
Worked example 1: the common widths, alignments and precisions
The first program prints one value per line in several shapes. Read each line and predict the output before you look below the code.
The output is:
42
[ 42]
[42 ]
[00042]
3.14
[ 3.142]
[Ada ]
[ Ada]
1,234,567
1,234,567.89
ff FF 10 101
+5 -5
1.234568e+04
1.235e+04
-0003.50
second then first
abc
Tea-----| 2.50|Here is what each part does.
The width is the minimum number of characters the value takes up. {42:5} pads 42 to 5 characters. Numbers align to the right by default, and text aligns to the left. The alignment < forces left alignment, so {42:<5} gives 42 . The 0 in front of the width pads a number with zeros, so {42:05} gives 00042. For numbers, the 0 pads with zeros on the left. For text it works differently, as the third practice exercise shows.
The precision on a decimal number is the count of digits after the point. .2f gives two digits, so 3.14159 becomes 3.14. For text, precision works in the other direction: .3 keeps at most three characters, so abcdef becomes abc. A text value longer than its width is never cut. {'Ada Lovelace':<3} prints the whole name, because the width is a minimum.
The , option adds thousands separators. Python always uses a comma and a point in an f-string, so the output is the same on every computer. That is a difference from some other languages, where the separator follows the country setting.
The positional form {1} then {0} with str.format lets you reuse the arguments in a different order. The number inside the braces is the position of the value. This is useful for translated messages, where the word order differs between languages.
The fill character comes before the alignment. In {'Tea':-<8}, the fill is -, the alignment is < and the width is 8, so the text Tea is followed by five dashes. The fill can be any character, including a space.
Worked example 2: a price table with a width computed at run time
Sometimes you do not know the width before the program runs. The longest item name decides how wide the first column must be. The program below measures the longest name first, then builds the table with that width. The width can be put inside a spec with a second pair of braces, as in {name:<{width}}.
The output is:
Item Qty Price
----------------------------
Apple 3 0.50
Banana bread 12 2.25
Milk 1 2.00
Total: 30.50
3 itemsThe header and each row use the same width for the first column, so the columns line up no matter which name is longest. The inner braces {name_width} are replaced by the value of the variable before the outer spec is used. That is why the table can change its width without any change to the spec text.
Look at the price for Milk. The stored value is 1.999, and the table shows 2.00. The .2f spec rounds the number to two decimal places when it prints it. The stored value does not change. The total is computed from the stored values, so it is 30.499 in full precision, and it prints as 30.50.
The line with "-" * (...) is a separator. It is the length of the table, computed from the same numbers as the widths. If you change a width in one place, change the separator too, or compute the separator from the same variable.
The last line uses str.format on a template. The empty braces {} take the next value, so the line prints 3 items. The same result comes from an f-string, f"{len(items)} items". Both forms are correct, and f-strings are the shorter choice.
Worked example 3: the older % operator and print
Python code written before f-strings used the % operator. It takes a template with % placeholders and a tuple of values, the same idea as printf in C. You will meet it in older code, so you should be able to read it.
The output is:
Tea | 2.50|
42|42 |00042
2 4
010.0The placeholder %-8s is a text value, left-aligned in 8 characters. The placeholder %6.2f is a decimal number, right-aligned in 6 characters, with two digits after the point. The placeholder %5d is a whole number in 5 characters. The flags work as they do in f-strings: - aligns left and 0 pads with zeros.
The % operator is still supported, but f-strings and str.format are clearer, and they can use the same spec for a value and for its width. Use f-strings in new code.
The print function is the usual way to write a line. It separates its values with a space and ends the line with a newline. Its two options change that. sep sets the separator between values, and end sets the text after the last value. Set end="" to print without a newline, which is useful when a row is built from several parts:
The output is:
Tea 2.50|
Coffee 3.75|
a, b, cEach row is built from two print calls, and the second one ends the line with | and a newline. Because the first call uses end="", the two parts stay on one line.
Rounding: the same number, different output
Rounding is where beginners are most often surprised, so it deserves its own table. The values below are printed with one decimal place with an f-string.
The output is:
0.25 -> 0.2
0.35 -> 0.3
2.5 -> 2.5The rule is that Python rounds the value stored in memory, not the decimal digits you typed. The number 0.25 is exactly one quarter, which is a tie between 0.2 and 0.3. Python rounds a tie to the even digit, so 0.25 becomes 0.2. The number 0.35 is stored as a value slightly below 0.35, so it rounds down to 0.3. Neither answer is a bug. Both follow from how binary floats are stored.
The same rule applies to whole numbers. f"{2.5:.0f}" gives 2, and f"{3.5:.0f}" gives 4, because both are ties that go to the even digit. A report that must round halves up needs a different method, such as the decimal module with an explicit rounding mode. For most output, the default is fine, and the lesson is to test the rounding you need with real values before you rely on it in a report.
A second case shows that stored values are not exact. f"{0.1 + 0.2:.20f}" prints 0.30000000000000004441. The sum is not exactly 0.3, and printing more digits shows the difference. Rounding to two places hides it, which is why money is often stored as whole cents rather than as floats.
Common mistakes
Mistake 1: a type letter that does not match the value. The spec d needs a whole number. Passing a decimal number raises an error:
The output is:
ValueError: Unknown format code 'd' for object of type 'float'The message names the code (d) and the type that was passed (float). The fix is to use f or to pass a whole number. The old % operator behaves differently and does not raise an error: "%d" % 3.5 gives 3, because %d truncates the decimal part. Use the type that matches your data, and do not rely on truncation to hide a bug.
Mistake 2: fewer values than placeholders. If the template has two placeholders and you pass one value, Python stops with an error. Both forms are shown here:
The output is:
IndexError: Replacement index 1 out of range for positional args tuple
TypeError: not enough arguments for format stringCount the placeholders and the values before you run the code. The first message names the position that had no value.
Mistake 3: an unknown type letter. A typo such as q is not a type. Python reports it when it runs the code:
The output is:
ValueError: Unknown format code 'q' for object of type 'str'Mistake 4: expecting the width to be a limit. The width is the minimum, not the maximum. The text Ada Lovelace does not shrink to fit a width of 3, and the output keeps the whole name. To cut text to a length, use the precision for text, as in .3, which keeps abc.
Mistake 5: a format string built from user input. A format spec is code in a small language. If a user types {0.__class__} into a form and you pass that text to str.format as the template, the program prints something you did not intend, and a crafted template can read attributes of objects. Build the template in your code, and put the user's text in a value.
Mistake 6: forgetting that a float shows its stored value. A value such as 1.999 shows as 2.00 with two decimal places, and a total built from such values can differ from the sum of the printed numbers. Compute with the full values, and round only in the output.
Practice
Try each exercise before you open its answer. Each answer shows the output so you can check your work.
- Write an f-string that prints the name
Tealeft-aligned in 12 columns, followed by the price 2.5 right-aligned in 8 columns with two decimal places.
Show the answer
print(f"{'Tea':<12}{2.5:>8.2f}")The output is Tea 2.50. The name takes 12 columns and the price takes 8, so the line is 20 characters long.
- What does
f"{0.4567 * 100:.1f}%"print? Explain each step.
Show the answer
It prints 45.7%. The multiplication gives 45.67, and .1f rounds it to one decimal place, which is 45.7. The % in the text after the spec is an ordinary character, because it is outside the braces. In the % operator, a single percent sign would be read as a placeholder, so you would write %% there.
- Predict the output of
print(f"{5:04}")and ofprint(f"{'5':04}"). Then run both and explain the difference.
Show the answer
The first line prints 0005, because the 0 before the width pads a number with zeros on the left. The second line prints 5000, because the value is a string. For text, the 0 acts as a fill character with left alignment, so the padding goes on the right. The text 5 is followed by three zeros.
- Write a function
money(cents)that prints an integer number of cents as dollars with a comma for thousands and two decimal places, such as1,234.50for123450.
Show the answer
def money(cents):
return f"{cents / 100:,.2f}"
print(money(123450))The output is 1,234.50. The division by 100 gives a float, and the spec ,.2f adds the thousands separator and two decimal places.
- Print the value 1234 as a whole number with eight digits, padded with leading zeros.
Show the answer
print(f"{1234:08d}")The output is 00001234. The 0 before the width fills with zeros, and 8 is the width. The type d is optional for an integer, so f"{1234:08}" gives the same result.
Summary
- A format spec goes after a colon inside braces, and it sets the fill, alignment, sign, width, precision and type of a value.
- The width is a minimum. Use
<or>to set the alignment, and a0before the width to pad numbers with zeros. A width never cuts a value short. - The precision controls decimal places for numbers and the maximum length for text.
- Python rounds the stored value, so a tie such as 0.25 goes to the even digit. Test the rounding you need before you depend on it.
- A type letter that does not match the value raises an error in f-strings and
str.format. The old%dtruncates a decimal instead of raising an error. - Put the width or precision in a variable with a nested pair of braces, and use
printwithendwhen a row is built from several parts.