Python String Formatting

Make output look professional with f-string format specifiers for decimals, thousands separators, percentages, padding and aligned tables.

Lesson 11 of 107Beginner18 min read

Printing the right answer is only half the job. Compare Total: 1234.5 with Total: £1,234.50, or a list of products whose prices wobble across the screen with one where every price lines up neatly in a column. Good formatting makes output easier to read and makes your programs feel finished.

You've already met f-strings, which put values into text with curly braces: f"Hello, {name}". They can do much more. After the value, you can add a colon and a format specifier that controls decimal places, thousands separators, percentages, width and alignment, all in a few characters.

In this lesson you'll learn the format specifiers you'll use most, build a neatly aligned receipt, use a handy shortcut for debugging, and see the older format() method you'll still find in existing code.

Decimal places and separators

Put a colon after the value inside the braces, then the format:

Python
price = 1234.5
print(f"{price:.2f}")    # 1234.50
print(f"{price:,.2f}")   # 1,234.50
print(f"{0.257:.1%}")    # 25.7%
  • .2f means "a fixed-point number with 2 decimal places". It rounds as well as pads.
  • , adds a comma between every three digits; _ adds underscores instead.
  • % multiplies by 100 and adds a percent sign, so .1% shows one decimal place.
  • :d is for whole numbers, and :e shows scientific notation, such as 1.23e+06.

Width and alignment

A number before the type sets the minimum width. Add an alignment character before it:

FormatMeaningResult for "tea"
{x:<8}Left align in 8 characterstea
{x:>8}Right align tea
{x:^8}Centre tea
{x:*^8}Centre, filled with ***tea***

Text is left-aligned by default and numbers right-aligned. Combine width with other options: {price:>10.2f} right-aligns a price in 10 characters with 2 decimals. {n:03d} pads a number with zeros, turning 7 into 007.

Signs, dynamic widths and repr

A few more options cover situations that come up in real reports:

Python
change = 4.2
print(f"{change:+.1f}")        # +4.2  (always show the sign)
print(f"{-4.2:+.1f}")          # -4.2

width = 12
print(f"[{'Total':>{width}}]") # [       Total]

name = "Ada"
print(f"{name!r}")             # 'Ada'
  • + forces a plus or minus sign, which is useful for changes and differences.
  • A width can come from a variable by nesting braces: {value:>{width}}. This lets you size columns to fit the longest item.
  • !r shows the repr() of a value. For strings this adds quotes, so you can spot hidden spaces: 'Ada ' is obviously different from 'Ada'.

Expressions and debugging

The braces can hold any expression, not just a variable: f"{qty * price:.2f}" or f"{name.upper()}". Keep them short, though; complex logic is easier to read on its own line.

Add = after an expression and Python prints the expression as well as its value, which is perfect for quick debugging:

Python
total = 42
print(f"{total=}")         # total=42
print(f"{total * 2 = }")   # total * 2 = 84

To show a literal curly brace, double it: f"{{not a value}}" prints {not a value}.

The format() method and dates

Before f-strings arrived in Python 3.6, code used the format() method. The format specifiers are exactly the same:

Python
"{} owes {:.2f}".format("Sam", 7.5)    # Sam owes 7.50

You may also see the very old % style, "%s is %d" % ("Sam", 30), in older tutorials. For new code, prefer f-strings.

Dates have their own specifiers. With a datetime value, f"{today:%d %B %Y}" gives 05 March 2025. You'll learn more in the dates and times lesson.

f-string rules that changed in Python 3.12

F-strings were added in Python 3.6, so they don't work on older versions. Python 3.12 relaxed several rules inside the braces, and it helps to know the difference because you will meet both kinds of code.

Before 3.12 you couldn't reuse the same quote character inside the braces, or put a backslash there:

Python
user = {"name": "Ada"}
print(f"Hi {user['name']}")    # works on every version
print(f"Hi {user["name"]}")    # only Python 3.12 and later

If your code must run on older versions, use the other quote type inside the braces, and calculate anything that needs a backslash, such as "\n".join(items), in a variable before the f-string.

Python String Formatting Syntax

Format specifier patterns

f"{value:.2f}"      # 2 decimal places
f"{value:,}"        # 1,000,000
f"{value:.1%}"      # 12.5%
f"{text:<10}"       # left align, width 10
f"{num:>8.2f}"      # right align, width 8, 2 decimals
f"{num:05d}"        # 00042
f"{value=}"         # value=...
"{:.2f}".format(value)

Python String Formatting Summary

  • Add a colon and a format specifier inside f-string braces: {value:spec}.
  • .2f gives two decimal places, , adds thousands separators and % shows a percentage.
  • A width with <, > or ^ aligns values into columns; 0 pads numbers with zeros.
  • {expression=} prints the expression and its value for debugging.
  • format() uses the same specifiers and appears in older code.

Python String Formatting Examples

Each example shows the code, the output it produces and what each part does.

Example 1

A neat receipt

Python
items = [("Coffee", 2, 2.80), ("Bagel", 1, 3.25), ("Juice", 3, 1.90)]

print(f"{'Item':<10}{'Qty':>5}{'Total':>10}")
total = 0
for name, qty, price in items:
    line = qty * price
    total += line
    print(f"{name:<10}{qty:>5}{line:>10.2f}")
print(f"{'TOTAL':<15}{total:>10.2f}")
Output
Item        Qty     Total
Coffee        2      5.60
Bagel         1      3.25
Juice         3      5.70
TOTAL               14.55
Line-by-line explanation
  1. Line 3Headings use the same widths as the data, so the columns line up.
  2. Line 8Name left-aligned in 10, quantity right-aligned in 5, line total in 10 with 2 decimals.
  3. Line 9TOTAL takes 15 characters, the width of the first two columns together.
Example 2

Big numbers and percentages

Python
visitors = 1284503
signups = 9876
rate = signups / visitors

print(f"Visitors: {visitors:,}")
print(f"Sign-ups: {signups:,}")
print(f"Conversion rate: {rate:.2%}")
Output
Visitors: 1,284,503
Sign-ups: 9,876
Conversion rate: 0.77%
Line-by-line explanation
  1. Lines 5 to 6The comma option makes large numbers easy to read.
  2. Line 7.2% multiplies by 100, rounds to 2 places and adds the percent sign.
Example 3

Order numbers and debugging

Python
for n in [7, 42, 315]:
    print(f"Order #{n:05d}")

width, height = 4, 2.5
print(f"{width * height = }")
Output
Order #00007
Order #00042
Order #00315
width * height = 10.0
Line-by-line explanation
  1. Line 205d pads each number with zeros to 5 digits.
  2. Line 5The = shortcut shows both the expression and its result.

Common Python String Formatting Mistakes

Rounding with round() and losing zeros

Don't
print(round(4.5, 2))       # 4.5, not 4.50
Do
print(f"{4.5:.2f}")        # 4.50

round() returns a number, which doesn't keep trailing zeros. Formatting does.

Forgetting the f

Don't
name = "Ada"
print("Hello, {name}")      # Hello, {name}
Do
print(f"Hello, {name}")     # Hello, Ada

Formatting too early

Don't
price = f"{19.99:.2f}"
total = price * 2           # '19.9919.99'
Do
price = 19.99
total = price * 2
print(f"{total:.2f}")       # 39.98

Python String Formatting in Real Projects

Formatting in practice

Command-line tools align columns so output is easy to scan, reports show currency with separators and two decimals, and log messages often use {variable=} while a developer is tracking down a bug. Data libraries such as pandas use the same format specifiers, so what you learn here carries straight into data analysis work.

Python String Formatting FAQ

Should I use f-strings, format() or % formatting?

Use f-strings for new code: they are the most readable and the fastest in typical use. str.format() is still handy when the template is stored separately from the values, for example in a configuration file. The % style is the oldest and appears mainly in legacy code and the logging module.

How do I show a currency symbol with two decimal places?

Put the symbol in the text and the format in the braces: f"£{price:,.2f}" gives £1,234.50. For money in different countries, where separators and symbol positions vary, use the locale module or a library such as Babel instead of hard-coding the format.

Does :.2f round or cut off the extra decimals?

It rounds. f"{2.346:.2f}" gives 2.35. Because floats are stored in binary, a value such as 2.675 may round down to 2.67. If you need exact decimal rounding, for example for invoices, use the decimal module.

Can I use an f-string with a dictionary or a method call?

Yes. Any expression is allowed inside the braces, so f"{user['name']}", f"{name.title()}" and f"{len(items)} items" all work. Keep the expressions short; if the logic needs more than one step, calculate it on the line before.

How do I print a table where the columns fit the data?

Work out the widest value first, for example width = max(len(name) for name in names), then use a nested width in the format: f"{name:<{width}}". Every row then lines up regardless of the longest name.

Python String Formatting Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the f-string so the price shows exactly two decimal places.

price = 3.5
print(f"£{price:}")
Predict the outputeasy

What does this print?

n = 1500000
print(f"{n:,}")
Predict the outputmedium

What does this print?

print(f"[{'ok':>5}]")
Multiple choicemedium

Which f-string shows 0.4 as 40.0%?

Predict the outputhard

What does this print?

x = 3
print(f"{x**2=}")

Python String Formatting Quiz

5 questions to check what you learned.

  1. What does f"{7:03d}" produce?

    • 7
    • 700
    • 007
    • 3d7
  2. Which alignment character centres text?

    • <
    • >
    • =
    • ^
  3. What does "{:>6.1f}".format(3.14159) produce?

    • "3.1 "
    • " 3.1" (right-aligned in 6 characters)
    • "3.14159"
    • " 3.14"
  4. How do you print a literal { inside an f-string?

    • {{
    • \{
    • '{'
    • It isn't possible
  5. Why is it better to format values at the moment you print them?

    • Formatting is slow
    • f-strings can only be used in print()
    • Formatted values are strings, so calculating with them gives wrong results or errors
    • Python forgets formatting after one line