Skip to content

Exercise 7 of 10 · Strings and formatting

The Scoreboard

What you will make

A game scoreboard where every name, score and bar lands in the same column, built from one line of text with the values sitting inside it.

The one new idea: f-strings build text with values inside it

Every receipt, timetable and report you have ever read is text with values dropped into it at fixed widths. f-strings are how Python writes that, and they are what working programmers reach for by default.

Go straight to the code ↓

Text with the values already inside it

Every print so far has taken its pieces separately and let the commas sort them out. print("Score", score) works, and it puts exactly one space between the word and the number.

One space is the problem. A comma always gives you one, never two, never none, and never the five spaces that would drop a number into a column. As soon as you care what a line looks like, handing over pieces and hoping is not enough.

An f-string is a single piece of text with the values already sitting in it. You write the letter f immediately before the opening quote, and then anywhere inside you write a pair of curly braces with a variable name between them. Python swaps each pair for whatever that variable holds.

Python
score = 12
print(f"Score: {score}")

That prints Score: 12. The f is what brings the braces to life. Leave it off and you get the braces themselves, printed as plain characters, with no error at all.

Why gluing the pieces together fails

The obvious alternative is +, and it is worth trying once so you recognise the message:

Python
print("Score: " + score)

Python stops with TypeError: can only concatenate str (not "int") to str. Concatenate means join end to end, str is Python's word for text and int is its word for a whole number. Python will join text to text all day. It will not quietly turn a number into text for you, because it has no way of knowing whether you meant to glue it on or add it up. f-strings settle that argument before it starts.

Widths, and the colon that introduces them

Inside the braces you may add a colon, and after the colon an instruction about how much room the value gets.

  • {player:<7} gives player a space seven characters wide and pushes it to the left, filling the rest with spaces.
  • {score:>3} gives score a space three characters wide and pushes it to the right.

Fixed width is the whole trick behind a column. Every name occupies seven characters whether it is Ana or Cleo, so whatever follows starts at the eighth character on every single row, and your eye reads a straight edge down the page. Right-aligning the score does the same job for numbers: 12 and 8 end in the same place, however many digits each has.

Read < and > as arrows pointing at the side the value is pushed against.

A worked example

A shelf label rather than a scoreboard, so the answer to this one stays yours to write:

Python
fruit = "Plum"
count = 7
print(f"{fruit} in stock: {count}")
print(f"[{fruit:<6}][{count:>4}]")

fruit = "Fig"
count = 112
print(f"[{fruit:<6}][{count:>4}]")
Output
Plum in stock: 7
[Plum  ][   7]
[Fig   ][ 112]

The square brackets are only there so the padding is visible. Plum is four characters in a six-wide space, so two spaces trail it; Fig is three, so three spaces do. Both closing brackets still land in the same column, and so do both numbers, even though one is a single digit and the other three.

Your turn

The editor holds a scoreboard with three players on it. Each player gets three lines of setup — a name, a score, and bar = "#" * score, which repeats the # character once per point. * does to text what it already does to numbers: it makes copies.

Press Run before changing anything. Ana and Ben print as tidy rows. Cleo's row is ragged, because her line is still the old comma style:

Python
print(player, score, bar)

Rewrite that one line as an f-string so her row matches the two above it. The widths you need are on screen in those rows. Change nothing else.

Then change Cleo's score to 11 and run it again. Her bar should grow and her row should stay in column — that is the proof the layout is being worked out rather than typed in.

If something goes wrong

The most likely slip is the missing f. You will know it instantly, because the row prints the braces at you word for word instead of the values. Nothing has broken; the quote simply needs an f in front of it.

If the program stops on a TypeError mentioning str and int, a + has crept in where the f-string should be. If it stops on a TypeError about <, a colon is missing inside the braces.

And if the row prints but sits in the wrong place, count the characters rather than guessing. A name that drifts right has the arrows the wrong way round; a score jammed against the name means the plain space between the two brace groups got lost. Nothing here can break, so change a number and run it again.

Write your code

Runs in your browser. Press Run (or Ctrl/Cmd+Enter) and the output is checked for you.

Ctrl/Cmd+Enter to run

Press Esc then Tab to move keyboard focus out of the code editor.

Ready
Output will appear here after you run your code.

The runtime is starting in the background. You can type now — it will be ready before you are.

The answer appears here once you have run your code at least once.

Things that often go wrong here

Leaving the f off the front of the quote
Without the f the braces are not special, so Python prints them as ordinary characters and the row reads {player:<7}{score:>3} {bar}. There is no error, because a line of text with curly brackets in it is a perfectly legal thing to want.
Joining the pieces with + instead
Writing player + score stops the program with TypeError: can only concatenate str (not "int") to str. Concatenate means join end to end, str means text and int means whole number. Python will join text to text happily, but it refuses to guess whether a number should be glued on or added up. That refusal is the reason f-strings exist.
Swapping the two arrows round
The row still prints, but backwards: the name gets shoved to the right of its seven characters and the score to the left of its three, so you get a ragged left edge and the score pressed up against the name instead of under the PTS heading.
Forgetting the colon and writing {player<7}
Without the colon Python reads the inside of the braces as a question — is the name smaller than seven — and stops with TypeError: '<' not supported between instances of 'str' and 'int'. The colon is the boundary between the value and the instructions about how to lay it out.

Longer explanation: read the full lesson. Want a blank editor instead? Open the Python playground.