Start.dev
Back to blog

Python f-strings explained: format numbers and text

Python f-strings put values straight into text. Learn decimals, thousands separators, percentages, column alignment and the {value=} debug trick.

Ariel Weinberger
Ariel Weinberger ยท 4 min read

How do you print a price with two decimals, line up a column of numbers, or show a percentage in Python without a pile of str() calls?

Short answer: use an f-string. Put f before the quotes, then any variable or expression inside {}. Add a colon and a format spec to control how it looks: f"{price:.2f}" gives two decimals, f"{name:<10}" pads to ten characters, and f"{rate:.1%}" shows a percentage.

By the end of this post you will drop values into text, format numbers for people to read, line up a small table, and debug with one handy shortcut. If strings are still new, start with F Strings (opens in a new tab).

The basics: put values inside {}

name = "Ana"
tickets = 3
print(f"{name} bought {tickets} tickets")  # Ana bought 3 tickets

The f before the opening quote turns the string into an f-string. Anything inside {} is evaluated and turned into text. That includes expressions, not just variable names:

print(f"{name.upper()} has {len(name)} letters")    # ANA has 3 letters
print(f"Total: {tickets * 12}")                     # Total: 36
print(f"{'sold out' if tickets > 2 else 'available'}")  # sold out

No str() calls, no + between pieces, and no counting %s placeholders. That's why f-strings replaced "%s" % value and .format() in most modern Python code.

Format numbers with a colon

After the value, add : and a format spec. These are the ones you'll use most:

price = 1234.5
rate = 0.075
score = 7
 
print(f"{price:.2f}")    # 1234.50       two decimal places
print(f"{price:,.2f}")   # 1,234.50      thousands separator
print(f"{rate:.1%}")     # 7.5%          multiplies by 100 and adds %
print(f"{score:03d}")    # 007           pad with zeros to 3 digits
print(f"{1234567:_}")    # 1_234_567     underscores instead of commas

Read .2f as "fixed-point, 2 digits after the dot". The , goes before the dot. And % does the multiplying for you, so pass 0.075, not 7.5. Formatting Numbers (opens in a new tab) practices each of these.

One rounding surprise to know about: Python rounds halves to the nearest even digit, so f"{2.5:.0f}" is "2" and f"{3.5:.0f}" is "4". Some decimals also can't be stored exactly, which can tip a value you'd expect to round up. For money, that's a reason to keep amounts as whole cents, or use the decimal module, and only format at the end.

Line things up with width and alignment

A number before the format type sets a minimum width. <, > and ^ choose left, right or centre alignment:

name = "Ana"
print(f"[{name:<8}]")    # [Ana     ]
print(f"[{name:>8}]")    # [     Ana]
print(f"[{name:^8}]")    # [  Ana   ]
print(f"[{name:*^8}]")   # [**Ana***]   fill character goes first

Put width and decimals together and you can print a tidy receipt:

items = [("Popcorn", 4.5), ("Soda", 2.25), ("Nachos", 6)]
 
for item, cost in items:
    print(f"{item:<10}{cost:>8.2f}")
Popcorn       4.50
Soda          2.25
Nachos        6.00

{item:<10} pads each name to ten characters, so every price starts in the same column. {cost:>8.2f} right-aligns each price in eight characters with two decimals, so the dots line up. This is where learners in Width And Alignment (opens in a new tab) get stuck most, and the trick is to read a spec left to right: fill, alignment, width, then decimals and type.

The width can come from a variable too, with nested braces: f"{price:>{width}.1f}".

Debug with {value=}

Add = after an expression and the f-string prints both the expression and its value:

score = 7
name = "Ana"
print(f"{score=}")       # score=7
print(f"{name=}")        # name='Ana'

It's the quickest way to check what a variable holds while you work, and you can still add a format spec after it: f"{price=:.1f}".

Other handy formats

from datetime import date
 
today = date(2026, 10, 21)
print(f"{today:%d %b %Y}")   # 21 Oct 2026
print(f"{255:x}")            # ff      hexadecimal
print(f"{12:b}")             # 1100    binary
print(f"{3:+d}")             # +3      always show the sign
print(f"{{braces}}")         # {braces}

Dates accept the same codes as strftime(), right inside the braces. And to print a literal { or }, double it.

Common mistakes

  • Forgetting the f. "Total: {total}" prints the braces as they are. Python won't warn you, so check the output.
  • Formatting too early. f"{price:.2f}" returns a string. Do your math on the number, then format it once, when you print or display it.
  • Passing a percentage already multiplied. f"{7.5:.1%}" gives 750.0%. Pass the fraction, 0.075.
  • Mixing up width and decimals. In {cost:8.2f}, 8 is the total width, including the dot and decimals, and 2 is the number of decimals.
  • Reusing the same quotes inside. f"{d["name"]}" only works on Python 3.12 and later. For code that runs on older versions, use the other quote type inside: f"{d['name']}".

Quick recap

  • f"...{value}..." drops any variable or expression into text.
  • After a colon, .2f sets decimals, , adds separators and % shows a percentage.
  • <, > and ^ with a width line values up into columns.
  • {value=} prints the name and the value, which is great for debugging.
  • Format at the end. Keep the number as a number until you display it.

Frequently asked questions

What is an f-string in Python?

A string literal with f in front, like f"Hi {name}". Python replaces each {} with the value of the expression inside it. F-strings have been in Python since version 3.6.

How do I format a number to 2 decimal places?

Use :.2f inside the braces: f"{price:.2f}". Add a comma for thousands separators: f"{price:,.2f}".

What's the difference between f-strings and format()?

They use the same format specs, so f"{x:.2f}" and "{:.2f}".format(x) give the same result. F-strings are shorter and put each value right where it appears, which makes them easier to read.

Can I use an f-string with a dictionary?

Yes. Use the other quote type for the key: f"{player['name']} scored {player['score']}". On Python 3.12 and later, the same quote type works too.

Practice

Try each one in your browser, with nothing to install: Formatting Numbers (opens in a new tab), then Width And Alignment (opens in a new tab), then put both to work in the Cinema Tickets (opens in a new tab) project. For quick reps on the loops that usually surround a formatted table, try the Python Loops challenges.

Where to go next

Formatting sits in the input and output module of Intermediate Python (opens in a new tab). If you're newer to Python, Python for Beginners (opens in a new tab) covers f-strings early on, and our 30-day Python roadmap shows what comes after.

Level up your coding skills

Jump into interactive courses and start building real-world projects.

Start learning

Related posts

Turn a for loop that builds a new list into one line. You will curve scores, keep the ones that pass, and fix the syntax error beginners hit.
Ariel Weinberger
Ariel Weinberger ยท 8 min read
A day-by-day Python roadmap for your first 30 days: what to learn, what to build each week, and the mistakes that stall most beginners.
Ariel Weinberger
Ariel Weinberger ยท 7 min read
Python lists, tuples and dictionaries compared with simple examples: how each one works, how they differ, and a quick rule for picking the right one.
Ariel Weinberger
Ariel Weinberger ยท 4 min read