Python f-Strings: Print Things People Can Actually Read
If a person reads your output, format it. f-strings let you mix text and values, round numbers, add thousands separators and line up a table, with very little code. Formatting is for people, not for data files.
If a person is going to read your output, format it. print(total) showing 1234.5666666666666 is not output. It's a raw number that someone now has to figure out. f-strings fix that with very little code, and you'll use them in almost every program you write.
Skip ahead: If you already use f-strings for variables and know :.2f, :, and :>10, jump to "Lining up a table".
First, do you need this?
Yes, if your program shows anything to a person: a receipt, a report, a log message, a summary in the terminal. No, if the value goes into a file or another program that expects raw data. A CSV file or a JSON API wants 1234.57, not ₱1,234.57. Formatting is for people. Data is for machines. Don't mix them.
You need Python 3.6 or newer. Anything you install today is far newer than that.
The basics: put the variable inside the text
Put f before the quote and the variable inside {}:
name = "Ana"
items = 3
total = 1234.5666666666666
print(f"{name} bought {items} items for {total}")
print(f"Next year {name} will be {28 + 1}.")
Ana bought 3 items for 1234.5666666666666
Next year Ana will be 29.
Anything inside {} is real Python. A variable, a calculation, a function call. Keep it short though. If the expression is long, compute it on its own line first and give it a name. The f-string is for showing values, not for doing the work.
⚠ Warning: Forget the f and Python prints the braces as normal text: "{name} bought {items} items". No error, no warning. It's the most common f-string mistake, and you'll only notice it when you look at the output.
Rounding: :.2f
The total above has too many decimals. Add a colon and a format after the value:
total = 1234.5666666666666
print(f"Total: {total:.2f}")
print(f"Total: {total:.0f}")
print(f"Rate: {0.0725:.1%}")
Total: 1234.57
Total: 1235
Rate: 7.2%
.2f means two decimals, fixed. .0f rounds to a whole number. % multiplies by 100 and adds the percent sign, so you store 0.0725 and show 7.2%.
Notice something. 0.0725 with one decimal became 7.2%, not 7.3%. Floats are stored in binary, so 0.0725 is really a tiny bit less than that, and it rounds down. For a report, that's fine. For money that has to add up to the cent, see the warning below.
⚠ Warning: Formatting only changes what is shown. f"{total:.2f}" does not change total. If you compute with money, round the actual value (or better, use the decimal module) before you add things up. Otherwise the shown totals and the real totals can be off by a centavo, and an accountant will find it.
Thousands separators: :,
salary = 1250000
price = 1234.5
print(f"Salary: {salary:,}")
print(f"Price: ₱{price:,.2f}")
Salary: 1,250,000
Price: ₱1,234.50
You can combine them: ,.2f is "comma separator, two decimals". The order matters. Comma first, then the decimals. .2f, is an error.
Lining up a table
This is where f-strings really pay off. A width after the colon pads the value with spaces. < aligns left, > aligns right, ^ centers:
rows = [
("Coffee", 2, 95.0),
("Ensaymada", 12, 35.5),
("Bottled water", 1, 20.0),
]
print(f"{'Item':<15}{'Qty':>5}{'Price':>10}{'Total':>12}")
print("-" * 42)
grand_total = 0
for name, qty, price in rows:
line = qty * price
grand_total += line
print(f"{name:<15}{qty:>5}{price:>10.2f}{line:>12,.2f}")
print("-" * 42)
print(f"{'Grand total':<30}{grand_total:>12,.2f}")
Item Qty Price Total
------------------------------------------
Coffee 2 95.00 190.00
Ensaymada 12 35.50 426.00
Bottled water 1 20.00 20.00
------------------------------------------
Grand total 636.00
The rule I follow: text left, numbers right. Right-aligned numbers line up their decimals, so people can compare them at a glance. Left-aligned numbers look like a ransom note.
The widths have to add up. 15 + 5 + 10 + 12 is 42, which is why the line is "-" * 42 and the grand total row is 30 + 12. Change one width, change the others.
💡 Tip: If a value is longer than its width, Python doesn't cut it. It just pushes the rest of the row to the right. Pick widths for your longest realistic value, not your example data.
Debugging with =
Put = after the name and Python prints the name too:
qty = 12
price = 35.5
print(f"{qty=}, {price=}")
print(f"{qty * price=:.2f}")
qty=12, price=35.5
qty * price=426.00
This needs Python 3.8 or newer. I use it all the time when something gives the wrong number. It's faster than typing print("qty:", qty) and you never mix up which value is which.
Quotes inside f-strings
If the f-string uses double quotes, use single quotes inside the braces, like f"{row['name']}". That always works. Python 3.12 and newer also accept the same quotes inside, but if someone runs your code on an older version, it breaks. Stick to the version that works everywhere.
Need literal braces in the output? Double them: f"{{total}} is {total}" prints {total} is 636.0.
Common mistakes
- Missing
f. Braces show up in the output. Check this first. - Formatting too early.
f"{price:.2f}"is text. Try to add it to another number and you get aTypeError. Keep numbers as numbers until the moment you print. - Doing math inside the braces. Short math is fine. Business logic inside an f-string is hard to read and hard to test.
- Using f-strings for SQL queries. Never put user input into SQL with an f-string. That is how SQL injection happens. Use the database library's parameters instead.
What happens if you skip this
Your program works, and nobody wants to read what it prints. Someone copies the numbers into a spreadsheet to make sense of them. Or worse, they misread 1234.5666666666666 as 12,345 and make a decision on it. Readable output is part of the job, not decoration.
About
Variables in text, rounding, thousands separators, aligned tables and the = debugging trick, each with code you can run. After this, your program's output is something a person can read without a calculator.
- Responsible
- John G
- Last Update
- 09/16/2026
- Reading Time
- 6 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.