Format Strings in Python
Learn to format strings using f-strings and format() in Python. This step-by-step tutorial covers core concepts, hands-on exercises, troubleshooting, and what to study next.
Focus: format strings with f-strings and format()
You’ve written Python code that prints variables — maybe with clunky string concatenation like name + ' is ' + str(age) + ' years old'. That works, but it’s error-prone, hard to read, and slow to write. Worse, when you try to insert floats, dates, or formatted numbers, the code explodes into a mess of str() calls and plus signs. Python’s f-strings and the .format() method solve this cleanly — letting you embed expressions directly inside strings, control alignment, pad with spaces or zeros, and format numbers with currency symbols or decimal places — all in one readable line.
The problem this lesson solves
Imagine you’re building a financial dashboard. You need to display account balances with two decimal places, right-aligned in a table, with a dollar sign. Without proper string formatting, you’d write something like:
balance = 1234.5
print("$" + str(round(balance, 2)).ljust(10))
Fragile, right? Miss a parenthesis or forget ljust() and your columns won’t line up. The real pain surfaces when you need:
- Dynamic content — variables, expressions, function calls inside strings
- Precision control — rounding decimals, padding numbers
- Alignment — left, right, center for tabular output
- Readability — one clear line vs. a spaghetti of concatenation
The format strings with f-strings and format() technique eliminates all that friction.
Core concept / mental model
Think of a format string as a template with placeholders — like fill-in-the-blank mad libs. You define the skeleton text, then inject specific values into the blanks.
- f-strings (Python 3.6+) let you write
f"Hello, {name}!". Thefprefix tells Python to evaluate anything inside{}as an expression and insert its string representation. .format()method uses the same curly-brace syntax but without thefprefix:"Hello, {}".format(name).
Both produce the same result. The mental model is simple: write string → mark insertion points with {} → provide the values. The work happens at runtime when Python evaluates those curly braces.
Pro tip: F-strings are faster than
.format()for most cases because they’re evaluated at compile time when possible..format()is more flexible for dynamic templates (like reading a format string from a config file).
How it works step by step
1. Basic variable insertion
name = "Alice"
age = 30
# f-string
print(f"{name} is {age} years old.")
# .format()
print("{} is {} years old.".format(name, age))
Output:
Alice is 30 years old.
Alice is 30 years old.
2. Expressions and calculations
You can put any expression inside {} — arithmetic, function calls, even inline conditionals:
price = 19.99
tax_rate = 0.08
print(f"Total with tax: ${price * (1 + tax_rate):.2f}")
Output:
Total with tax: $21.59
The :.2f after the colon is a format specifier — 2 decimal places, f for fixed-point float.
3. Named placeholders with .format()
Use {name} instead of {} for clarity, especially with many values:
person = {"name": "Bob", "job": "engineer"}
print("{name} works as a {job}.".format(**person))
Output:
Bob works as an engineer.
4. Format specifiers: padding, alignment, numeric types
The format mini-language uses : followed by:
- Alignment: < (left), > (right), ^ (center)
- Width: number of characters
- Fill character: optional character before alignment, e.g. 0 for zero-padding
- Type: d (integer), f (float), x (hex), % (percentage)
value = 42
print(f"|{value:>5}|") # right-aligned, width 5
print(f"|{value:0>5}|") # zero-padded
print(f"|{value:<5}|") # left-aligned
print(f"|{value:^5}|") # centered
Output:
| 42|
|00042|
|42 |
| 42 |
Hands-on walkthrough
Let’s build a simple inventory report for a bookstore. We’ll use both f-strings and .format() to format prices, quantities, and totals.
Example 1: Basic f-string report
items = [
("Python 101", 2, 39.99),
("Data Science Handbook", 5, 59.99),
("Machine Learning for Beginners", 1, 89.99)
]
print("Inventory Report")
print("=" * 50)
for name, qty, price in items:
total = qty * price
print(f"{name:35s} {qty:3d} @ ${price:6.2f} = ${total:7.2f}")
Output:
Inventory Report
==================================================
Python 101 2 @ $ 39.99 = $ 79.98
Data Science Handbook 5 @ $ 59.99 = $ 299.95
Machine Learning for Beginners 1 @ $ 89.99 = $ 89.99
Example 2: Named placeholders with .format()
template = "{book:30s} | {qty:3d} units | ${price:6.2f} each"
for name, qty, price in items:
print(template.format(book=name, qty=qty, price=price))
Output:
Python 101 | 2 units | $ 39.99 each
Data Science Handbook | 5 units | $ 59.99 each
Machine Learning for Beginners| 1 units | $ 89.99 each
Example 3: Dynamic template from a configuration
Sometimes you need to load format strings from a file or database. .format() shines here:
# config could come from a JSON file
config_format = "{name:>20s} {age:>3d}"
users = [("Alice", 30), ("Bob", 25), ("Charlie", 35)]
for name, age in users:
print(config_format.format(name=name, age=age))
Output:
Alice 30
Bob 25
Charlie 35
Compare options / when to choose what
| Method | When to use | Example |
|---|---|---|
| f-string | Static templates — you control the string at write time | f"Hello, {name}!" |
.format() |
Dynamic templates — format string comes from a variable or config | "Hello, {}".format(name) |
% formatting |
Legacy code (pre‑Python 2.6) | "%s is %d" % (name, age) |
| string.Template | Simple substitution with $ syntax, safe for user-provided templates |
Template("$name is $age").substitute(...) |
Recommendation: Default to f-strings for new code. Use .format() when you need to separate the template (e.g., loadable from files). Avoid % formatting unless maintaining legacy code.
Troubleshooting & edge cases
1. Missing values for placeholders
print("{} {} {}".format(1, 2)) # IndexError: Replacement index 2 out of range
Fix: Ensure you provide exactly as many values as placeholders, or use named placeholders.
2. KeyError in .format() with dict unpacking
data = {"name": "Alice"}
print("{name} is {age}".format(**data)) # KeyError: 'age'
Fix: Use .get() or default values: "{name} is {age}".format(**data, age="unknown")
3. Float precision surprises
print(f"{1/3:.2f}") # 0.33 – correct
print(f"{1/3:.20f}") # 0.33333333333333331483 – floating-point artifact
Fix: For financial calculations, use decimal.Decimal before formatting. For display, rounding to 2–4 places is usually fine.
4. Missing f prefix on f-strings
name = "Alice"
message = "{name}" # prints the literal {name}
print(message)
Output: {name} (not Alice)
Fix: Add the f prefix: f"{name}"
What you learned & what's next
You now understand the core idea behind format strings with f-strings and format(). You can:
- Insert variables and expressions into strings cleanly
- Control alignment, padding, and numeric precision
- Choose between f-strings (static) and .format() (dynamic) based on your needs
- Troubleshoot common issues like missing values and float precision
With this skill, you can generate reports, format user-facing messages, and build data tables without string concatenation headaches. Up next, you’ll explore string methods — slicing, splitting, joining, and searching — so you can parse and manipulate text data efficiently.
Remember: format strings are the bridge between raw data and beautiful output. Master them, and your code reads like a story, not a puzzle.
Practice recap
Write a script that reads a list of product dictionaries (name, price, quantity) and prints a receipt. Use f-strings to format the price to two decimals, right-align totals in a column, and center the header. Try converting one row to use .format() with named placeholders to see the difference.
Common mistakes
- Forgetting the
fprefix on f-strings — without it,{variable}stays as literal text. - Mismatching placeholder count with
.format()— Python raises anIndexErrorif you have more placeholders than arguments. - Assuming f-strings are safe when the template comes from user input — avoid
eval()-like scenarios; use.format()with only safe keys. - Confusing
:alignment specifiers: left<, right>, center^— easy to mix up and produce misaligned columns.
Variations
- Use
formatted string literals(f-strings) for speed and readability in static templates. - Use the
%operator for legacy Python 2 codebases — but avoid in new Python 3 projects. - Use
string.Templatefor user-supplied templates where you want to restrict placeholder syntax to$varor${var}.
Real-world use cases
- Generate dynamic email templates that insert user name, order total, and date — f-strings make it readable and fast.
- Format currency values in financial reports with two decimal places and dollar signs using
:.2fspecifier. - Build aligned columns for CLI tools (like
pstables) using width specifiers and alignment characters in.format().
Key takeaways
- F-strings (prefix
f) evaluate expressions inside{}at runtime — use them for static templates. - The
.format()method works with dynamic templates and supports named placeholders. - Format specifiers after
:control alignment (<,>,^), width, and numeric types (d,f,%). - Float formatting with
:.2frounds numbers — for exact decimals, usedecimal.Decimalfirst. - Always match the number of placeholders and values when using
.format()— mismatch raisesIndexError.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.