🧵
Coding🎓 Ages 14-18Beginner 10 min read

F-Strings and String Formatting in Python

Master Python f-strings: embed variables, run expressions, format numbers and decimals, pad and align text, with clear line-by-line examples and a receipt project.

Key takeaways

  • An f-string starts with f before the quote and puts variables inside {curly braces}
  • You can run expressions inside the braces, like {price * 2}
  • Format specifiers after a colon control decimals, padding and alignment, like {value:.2f}
  • F-strings are clearer and faster than gluing strings together with +

The problem f-strings solve

Suppose you have a name and a score and want to print a friendly message. Beginners often write:

name = "Ada"
score = 95
print("Hello " + name + ", you scored " + str(score) + " points")

This works, but it is fiddly: you must add spaces by hand, glue pieces with +, and wrap the number in str() or Python complains. F-strings make the same line far cleaner.

This lesson assumes you know the basics of working with text and strings and Python input and output.

Your first f-string

name = "Ada"
score = 95
print(f"Hello {name}, you scored {score} points")
# Hello Ada, you scored 95 points

Two things make this an f-string:

  1. The letter f immediately before the opening quote.
  2. The {name} and {score} placeholders. Python replaces each one with the variable's value, converting numbers to text automatically. No +, no str().

Expressions inside the braces

You are not limited to plain variable names. Any Python expression works inside the braces:

price = 8
print(f"Two cost {price * 2} pounds")     # Two cost 16 pounds

word = "python"
print(f"Shout: {word.upper()}!")          # Shout: PYTHON!

print(f"3 plus 4 is {3 + 4}")             # 3 plus 4 is 7

Python evaluates whatever is between the braces, then drops the result into the string. This is why {price * 2} shows 16, not the literal text.

Formatting numbers

The most useful feature is the format specifier, written after a colon inside the braces. It controls how a value is displayed.

Decimal places

pi = 3.14159
print(f"Pi is about {pi:.2f}")     # Pi is about 3.14
print(f"Pi is about {pi:.4f}")     # Pi is about 3.1416

:.2f means "format as a float with 2 digits after the decimal point". Python rounds the displayed value for you. This is perfect for money and measurements.

Thousands separators

big = 1234567
print(f"Population: {big:,}")      # Population: 1,234,567

The :, inserts commas to group thousands, making large numbers readable.

Percentages

ratio = 0.732
print(f"Score: {ratio:.0%}")       # Score: 73%

:.0% multiplies by 100, adds a percent sign, and shows no decimals.

Padding and aligning text

Format specifiers can also set a width and alignment, which is ideal for tidy tables:

items = [("Apple", 3), ("Banana", 12), ("Cherry", 5)]
for name, qty in items:
    print(f"{name:<8}{qty:>3}")

Output:

Apple     3
Banana   12
Cherry    5
  • {name:<8} left-aligns the name in a field 8 characters wide.
  • {qty:>3} right-aligns the quantity in a field 3 characters wide, so the numbers line up neatly.

This loop uses tuple unpacking, which you can revisit in Python tuples and sets.

Worked example: a shop receipt

Let's combine these tools into a small program that prints a clean receipt.

cart = [
    ("Notebook", 2, 3.50),
    ("Pen", 5, 0.99),
    ("Eraser", 1, 1.20)
]

total = 0
print(f"{'Item':<10}{'Qty':>4}{'Price':>8}")
print("-" * 22)

for name, qty, price in cart:
    line_cost = qty * price
    total += line_cost
    print(f"{name:<10}{qty:>4}{line_cost:>8.2f}")

print("-" * 22)
print(f"{'TOTAL':<10}{'':>4}{total:>8.2f}")

Output:

Item       Qty   Price
----------------------
Notebook     2    7.00
Pen          5    4.95
Eraser       1    1.20
----------------------
TOTAL              13.15

Step by step:

  • The header uses left and right alignment so columns line up.
  • line_cost = qty * price calculates each row's cost; total += line_cost adds it to the running total.
  • {line_cost:>8.2f} combines width (8), alignment (right) and two decimal places — all in one specifier.
  • "-" * 22 repeats a dash 22 times to draw a divider, a handy string trick.

Every column lines up because each value sits in a fixed-width field. Try doing that with + joins and you will appreciate f-strings instantly.

Common mistakes

  • Forgetting the f. Without it, "{name}" prints the literal braces instead of the value.
  • Putting the colon in the wrong place. Format instructions go after the value and a colon: {value:.2f}, never {.2f:value}.
  • Confusing display with storage. {pi:.2f} only changes how pi looks; the variable still holds the full number.
  • Quote clashes. Inside a double-quoted f-string, use single quotes for any inner strings.

Try it yourself

  1. Ask the user for a temperature in Celsius and print it converted to Fahrenheit with one decimal place using an f-string.
  2. Print a multiplication table for the number 7 where every product is right-aligned in a field 4 wide.
  3. Build a receipt for your own list of three items and show the total with a thousands separator and two decimals.

When you are comfortable, deepen your text skills with the Python string methods deep dive.

Quick quiz

Test yourself and earn XP

How do you start an f-string?

What does f"{2 + 3}" produce?

What does the specifier .2f do in f"{x:.2f}"?

What separates a value from its format instructions inside the braces?

Why are f-strings preferred over using + to join text and numbers?

FAQ

F-strings were added in Python 3.6, so any modern Python supports them. If you are on an older version you would use the older .format() method instead.

Yes, but use a different style than the outer quotes. If the f-string uses double quotes outside, use single quotes inside the braces, or the reverse, to avoid confusion.

Formatting with .2f only changes how the number is displayed as text; the underlying value is unchanged. round() actually changes the number you store.