Skip to content
CCC Python Course

Output formatting

Module
M1.11
Lesson
1 of 1
Reading time
5 min

In this lesson

  • Build a formatted string with an f-string, inserting a value directly inside {}.
  • Zero-pad a number and fix how many decimal places a float shows, with an f-string format spec.
  • Round a number to the nearest whole value or a chosen number of decimals with round().
  • Join a list into one line with no trailing separator, using " ".join(map(str, ...)).

Earlier modules built output with print() and commas, which inserts a single space between values and nothing else. Many CCC problems ask for an exact shape instead: a fixed number of digits, a fixed number of decimal places, or a line of values with no trailing separator. This lesson covers Python's tools for that exact shape.

Building a string with an f-string

An f-string is a string literal written f"...", where anything inside {} is evaluated and inserted into the text.

examples/fstring_basics.py
name = "Ana"score = 92print(f"{name} scored {score}")

Output

Ana scored 92
Inserting two values directly into a string

f"{name} scored {score}" inserts name and score directly into the text, without needing separate print() arguments or + to join pieces together.

Zero-padding and fixed decimals

Adding a format spec after a colon inside {} controls exactly how a value prints. :02d pads a whole number with leading zeros to at least two digits.

examples/zero_padding.py
hours = 7minutes = 5print(f"{hours:02d}:{minutes:02d}")

Output

07:05
Padding hours and minutes to two digits each

f"{hours:02d}" prints 07, not 7, since 02d asks for at least two digits, padded with a leading zero. :.1f and :.2f fix a float to exactly that many decimal places.

examples/fixed_decimals.py
distance = 18.5print(f"{distance:.1f}")print(f"{distance:.2f}")

Output

18.5
18.50
Fixing a float to one and then two decimal places

f"{distance:.1f}" prints 18.5, and f"{distance:.2f}" prints 18.50, the same value with a different number of decimal places shown. A problem that asks for an answer "to one decimal place" wants exactly this format, not however many digits Python would print on its own.

Rounding with round()

round() rounds a number instead of just formatting how it displays. With one argument it rounds to the nearest whole number; with a second argument, it rounds to that many decimal places.

examples/round_builtin.py
value = 18.5print(round(value))print(round(3.14159, 2))

Output

18
3.14
Rounding to the nearest whole number, and to two decimal places

round(18.5) gives 18, a whole number, unlike f"{distance:.1f}", which keeps the decimal place but changes nothing about the value. round(3.14159, 2) gives 3.14, the value itself rounded, not merely displayed differently.

Joining a list with no trailing separator

Printing a list's values one per print() call, or with an extra separator after every value, often leaves an unwanted separator at the end of the line. " ".join(map(str, xs)) joins every value in xs with a space between them, and nothing extra at either end.

examples/join_no_trailing.py
nums = [3, 5, 2]print(" ".join(map(str, nums)))print(",".join(map(str, nums)))

Output

3 5 2
3,5,2
Joining a list with a space, then with a comma, and no trailing separator either way

map(str, nums) converts every number in nums to text first, since .join() only works on strings. " ".join(...) then places exactly one space between each pair of values, with no leading or trailing space. Changing the separator to "," needs no other change to the pattern.

Aligning text in columns

A format spec can pad text as well as numbers. :<10 pads a value with spaces on the right until it is at least 10 characters wide, :>10 pads on the left, and :^10 centers it. This comes up whenever a problem wants output lined up in columns, such as a name followed by a score, both fields a fixed width apart.

f"{name:<10}{score:>5}" prints name left-aligned in a 10-character field. It prints score right-aligned in a 5-character field. Every row lines up under the last one, no matter how long each name or score happens to be. Numbers usually look better right-aligned. That lines up the ones digit of every row. Text usually looks better left-aligned instead, since that lines up where each word starts.

Combining a width with a numeric format spec is also common: :>5.2f right-aligns a float in a 5-character field, with exactly two decimal places. The spec always has two parts in the same order: alignment and width first, the numeric format second. Once you have written one such spec, the rest follow the same shape.

Choosing between an f-string and % or .format()

Python has two older ways to build a formatted string: the % operator, as in "%d items" % count, and the .format() method, as in "{} items".format(count). Both still work, and you will see them in other people's code. An f-string does the same job with less to type. The value sits right where it is used, instead of matched up by position further down the line. New code should reach for an f-string first. The other two are worth recognizing, not necessarily writing.

A trap: an integer's value printed as a float

/ always gives a float, even when the exact answer is a whole number, and printing that float shows a trailing .0 a problem may not want.

examples/int_vs_float_output.py
total = 8 / 2print(total)print(int(total))

Output

4.0
4
The same value printed as a float, then as an int

8 / 2 is 4.0, and printing it directly shows 4.0, not 4. int(total) converts it to a whole number first, so it prints as 4. Whenever a problem's expected output is a whole number, check whether your own answer is still a float underneath before you print it.

Getting the exact format a problem asks for

Read the problem statement's sample output character by character before you write a single line of formatting code. "Round to two decimal places" and "print exactly two decimal places" sound alike but are not. The first only constrains the value. The second constrains what the printed text looks like, trailing zero included. round(3.1, 2) gives 3.1, a value with only one decimal digit, and printing it directly shows 3.1, not 3.10. Only a format spec such as :.2f guarantees the second digit prints, even when it happens to be zero.

The same care applies to separators and spacing. A judge compares your program's output to the expected output byte for byte. An extra trailing space, a missing newline, or a comma where the problem wanted a plain space is a wrong answer, even when every number in the line is correct. When in doubt, copy the sample output's exact spacing, rather than guessing at what "looks reasonable."

Recap

This lesson covered building a string with an f-string, zero-padding a number and fixing a float's decimal places with a format spec, and rounding with round(). It also covered joining a list into one line with no trailing separator, and the trap of printing a whole-number float with an unwanted .0. The two problems below both need output shaped exactly to what the problem asks for.

Practice

Try these on the judges. Each link opens the problem on WMOJ or DMOJ.

  1. 2021 S1
    Crazy Fencing (opens on WMOJ in a new tab) WMOJ

    Add up the areas of several trapezoid-shaped fence pieces, printing the exact total.

  2. 2018 J3
    Are we there yet? (opens on DMOJ in a new tab) DMOJ

    Print a small table of numbers as evenly spaced, space-separated rows.

    Why DMOJ: Tries the same kind of formatted output on DMOJ, which holds 2014 to 2020.