f-strings & String Formatting

Reviewed & published by Brayan K

Almost every program eventually needs to weave data into text — a greeting with the user's name, a receipt with a price, a log line with a timestamp. f-strings are Python's clean, fast, and readable way to do exactly that.

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

By the end of this lesson you'll format money, percentages, tables, and dates like a professional, and you'll understand the format spec mini-language that powers it all.

What You'll Learn in This Lesson

1 What Is an f-string?

An f-string (formatted string literal) is a normal string with the letter f placed right before the opening quote. Inside it, anything you wrap in curly braces { } is treated as Python code, evaluated, and slotted into the text.

name = "Ada"
age = 36

# Old, clunky way — lots of quotes and str() calls
print("Hi " + name + ", you are " + str(age) + " years old.")

# f-string way — reads exactly like the output
print(f"Hi {name}, you are {age} years old.")

# Output (both lines):
# Hi Ada, you are 36 years old.

Notice you never call str() inside an f-string — Python converts each value to text for you automatically. That single feature removes a whole category of TypeError bugs.

🌍 Why f-strings Win in Real Code

The same line of output, written three ways. Read each and decide which one you'd want to maintain at 2am during an outage:

2 You Can Put Any Expression Inside the Braces

The braces are not limited to bare variable names. You can do arithmetic, call methods, index into a list, or even run a small conditional — all inline:

width = 4
height = 5
name = "ada lovelace"
scores = [88, 92, 79]

print(f"Area is {width * height}")            # Area is 20
print(f"Title: {name.title()}")               # Title: Ada Lovelace
print(f"First score: {scores[0]}")            # First score: 88
print(f"Average: {sum(scores) / len(scores)}") # Average: 86.33333333333333
print(f"Pass? {'yes' if min(scores) >= 80 else 'no'}")  # Pass? no

3 The Format Spec — Control How Values Look

After a value you can add a colon and a format spec: {value:spec}. This is where f-strings go from convenient to genuinely powerful. Money, percentages, and aligned columns all come from here.

The most common spec by far is :.2f — "show this float with exactly two digits after the decimal point", perfect for currency. The :, spec instantly makes large numbers human-readable.

📐 Alignment & Padding — Building Neat Tables

Use < (left), > (right), or ^ (center) with a width to line text up into columns — the trick behind every console report:

items = [("Coffee", 3.5), ("Sandwich", 6.25), ("Tea", 2.0)]

print(f"{'Item':<12}{'Price':>8}")
print("-" * 20)
for name, price in items:
    print(f"{name:<12}{price:>8.2f}")

# Output:
# Item           Price
# --------------------
# Coffee          3.50
# Sandwich        6.25
# Tea             2.00

{name:<12} left-aligns the name in a 12-character field; {price:>8.2f} right-aligns the price in an 8-character field with 2 decimals. You can even pick the fill character: {'':*^20} centers nothing in a 20-wide field of asterisks.

# Format a mini receipt with f-strings
item = "Wireless Mouse"
price = 24.999   # note the third 9: .2f ROUNDS, so this prints 25.00, not 24.99
qty = 3
tax_rate = 0.2

subtotal = price * qty
tax = subtotal * tax_rate
total = subtotal + tax

print(f"{'RECEIPT':^30}")
print("=" * 30)
print(f"{item:<20}{price:>10.2f}")
print(f"{'Quantity':<20}{qty:>10}")
print(f"{'Subtotal':<20}{subtotal:>10.2f}")
print(f"{'Tax (20%)':<20}{tax:>10.2f}")
print("-" * 30)
print(f"{'TOTAL':<20}{total:>10.2f}")

# ✅ Expected output:
#            RECEIPT            
# ==============================
# Wireless Mouse           25.00
# Quantity                     3
# Subtotal                 75.00
# Tax (20%)                15.00
# ------------------------------
# TOTAL                    90.00

4 The = Debug Trick (Python 3.8+)

Put an = right after an expression inside the braces and Python prints both the expression text and its value. It's the fastest way to sprinkle debug prints:

x = 7
y = 12

print(f"{x=}")           # x=7
print(f"{y=}")           # y=12
print(f"{x + y=}")       # x + y=19
print(f"{x * y = }")     # x * y = 84   (spaces are preserved)

No more typing the variable name twice. Reach for this any time you'd otherwise write print("x", x).

5 Printing Literal Curly Braces

Because { } means "evaluate this", you need a way to print an actual brace. The rule: double it.

value = 5

print(f"A set looks like {{1, 2, 3}}")  # A set looks like {1, 2, 3}
print(f"{{{value}}}")                   # {5}
# The outer doubled braces are literal; the inner pair is the placeholder.

🧵 Multiline f-strings & the Older Styles

Multiline f-strings with triple quotes

name = "Maya"
plan = "Pro"
seats = 5

email = f"""
Hi {name},

Your {plan} plan is active with {seats} seats.
Thanks for subscribing!
"""
print(email)

You'll still see these in older codebases

name, age = "Ada", 36

# .format() — Python 2.7+ / 3.x
print("Hi {}, age {}".format(name, age))
print("Hi {n}, age {a}".format(n=name, a=age))

# % formatting — the oldest, C-style
print("Hi %s, age %d" % (name, age))

# All print: Hi Ada, age 36

Know how to read them, but write f-strings in new code.

🧩 Reorder Challenge

These scrambled lines should compute a discounted price and print a clean formatted line. Reorder them so the final output is Final: $42.50 (15% off).

B → D → A → C → E

Why: discount (A) needs both price (B) and rate (D) to exist first. final (C) depends on the discount, and the f-string (E) reads both final and rate. The :.0% spec turns 0.15 into 15%.

🧠 Quick Recall

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

7 — the braces evaluate the full expression 3 * 2 + 1, following normal operator precedence.

2. What is the output?

p = 0.5
print(f"{p:.0%}")

50% — the % spec multiplies by 100 and appends a percent sign; .0 means zero decimal places.

3. What does this log?

n = 5
print(f"{{{n}}}")

{5} — the outer doubled braces are literal, the inner single pair interpolates n.

📋 Format Spec Quick Reference

SpecMeaningExample → Result
.2f2 decimal placesf"{3.1:.2f}" → 3.10
,Thousands separatorf"{12000:,}" → 12,000
.1%Percentagef"{0.5:.1%}" → 50.0%
>10Right-align width 10f"{x:>10}"
05dZero-pad to 5f"{42:05d}" → 00042
{x=}Debug name + valuex=7

🎯 Mini Challenge: Invoice Line

Build a single formatted invoice line for a product, then experiment with the format specs.

Lesson complete — you format like a pro now!

You can embed expressions, format money and percentages, align columns, use the = debug trick, and read the older .format() and % styles. f-strings will appear in nearly every Python file you ever write.

Practice quiz

What does the 'f' in f-string stand for?

  • fast
  • function
  • formatted
  • final

Answer: formatted. The f stands for 'formatted' — a formatted string literal.

Given x = 3, what does f'{x * 2 + 1}' print?

  • 7
  • 6
  • 9
  • x * 2 + 1

Answer: 7. The braces evaluate the full expression 3*2+1 = 7.

What does f'{0.5:.0%}' print?

  • 0.5%
  • 5%
  • 0%
  • 50%

Answer: 50%. The % spec multiplies by 100 and .0 means zero decimals, giving 50%.

Given n = 5, what does f'{{{n}}}' print?

  • {{5}}
  • {5}
  • 5
  • {n}

Answer: {5}. The outer doubled braces are literal and the inner pair interpolates n, giving {5}.

What does f'{1299.5:.2f}' print?

  • 1299.50
  • 1299.5
  • 1,299.50
  • 1300

Answer: 1299.50. .2f shows exactly two decimal places: 1299.50.

What does f'{9500000:,}' print?

  • 9500000
  • 9.5M
  • 9,500,000
  • 9 500 000

Answer: 9,500,000. The , spec adds thousands separators: 9,500,000.

What does f'{42:05d}' print?

  • 42
  • 00042
  • 42000
  • 42

Answer: 00042. 05d zero-pads the integer to a width of 5: 00042.

How do you print a literal single curly brace inside an f-string?

  • Escape it with a backslash
  • Use a backtick
  • You cannot
  • Double it: {{ or }}

Answer: Double it: {{ or }}. Doubling the brace ({{ or }}) produces one literal brace.

Given x = 7, what does the debug syntax f'{x=}' print?

  • 7
  • x=7
  • x
  • x = 7

Answer: x=7. The = debug form prints both the expression text and its value: x=7.

Which alignment spec right-aligns a value in a 10-character field?

  • {x:<10}
  • {x:^10}
  • {x:>10}
  • {x:10<}

Answer: {x:>10}. > means right-align; <10 left-aligns and ^10 centers.

Continue this course

Frequently asked questions

What does the f in f-string actually mean?

The f stands for 'formatted'. Putting f (or F) immediately before the opening quote turns an ordinary string into a formatted string literal, which tells Python to evaluate any expression inside curly braces and drop the result straight into the text.

What's the difference between f-strings, .format(), and % formatting?

They all build strings from values, but f-strings (Python 3.6+) are the modern default: the variable name sits right where it appears in the output, so the code reads like the result. .format() and the older % style still work and you'll see them in legacy code, but they separate the placeholders from the values, which is harder to read.

How do I show a literal curly brace inside an f-string?

Double them. Write {{ to print a single { and }} to print a single }. For example f'{{{x}}}' with x = 5 prints {5} — the doubled braces are literal and the single pair around x is the placeholder.

Why does my f-string print a $ sign or the word None unexpectedly?

A $ is just a literal character in Python f-strings — unlike some other languages, Python placeholders use {} not ${}. If you see None, you probably interpolated a variable that was never assigned a real value, or a function that returns nothing (it implicitly returns None).

Can I format numbers, dates, and alignment inside an f-string?

Yes. After the value add a colon and a format spec: {price:.2f} for two decimals, {n:,} for thousands separators, {pct:.1%} for a percentage, and {name:>10} to right-align in a 10-character field. These specs are the real power of f-strings.