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.
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:
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}givesplayera space seven characters wide and pushes it to the left, filling the rest with spaces.{score:>3}givesscorea 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:
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}]")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:
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.
Press Esc then Tab to move keyboard focus out of the code editor.
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.