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
- The f-string syntax and why it beats + concatenation
- Embedding any expression, not just variables
- Formatting numbers: decimals, commas, percentages
- Padding and aligning text into neat columns
- Self-documenting f-strings with the = debug syntax
- The older .format() and % styles you'll meet in old code
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? no3 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.004 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 36Know 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
| Spec | Meaning | Example → Result |
|---|---|---|
| .2f | 2 decimal places | f"{3.1:.2f}" → 3.10 |
| , | Thousands separator | f"{12000:,}" → 12,000 |
| .1% | Percentage | f"{0.5:.1%}" → 50.0% |
| >10 | Right-align width 10 | f"{x:>10}" |
| 05d | Zero-pad to 5 | f"{42:05d}" → 00042 |
| {x=} | Debug name + value | x=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
- Previous: Lambda Functions
- Next: String Methods — split, join, strip, replace and the string methods you'll use daily
- Quick reference: Python cheat sheet › Strings
- From the blog: 10 Python Tips Every Beginner Should Know
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.