Python f-Strings Formatting Guide
A complete guide to Python f-strings — basic interpolation, format specifiers for numbers and dates, alignment and padding, debug syntax, and when to choose other formatting methods.
What you'll learn
- ✓How f-strings interpolate Python expressions
- ✓Formatting numbers, percentages, and dates
- ✓Aligning and padding text
- ✓The =-debug syntax for quick prints
- ✓When to use %-formatting or str.format instead
Prerequisites
- •Basic Python familiarity
Formatted string literals, almost always called f-strings, are the cleanest way to build strings in modern Python. They are fast, readable, and capable of nearly every formatting trick you would ever want. Once you learn the format specifier mini-language, they replace %-formatting and str.format for almost every use.
The Basics
Prefix a string literal with f (or F) and Python evaluates any expression between curly braces.
name = "Ada"
age = 36
print(f"{name} is {age} years old")
# Ada is 36 years old
The expression inside the braces can be anything that evaluates to a value — variables, attribute access, method calls, arithmetic.
items = ["pen", "paper", "ink"]
print(f"You have {len(items)} items: {', '.join(items)}")
Formatting Numbers
After the expression, you can add a colon and a format specifier. For numbers, the common cases are precision, thousands separators, and percentages.
pi = 3.14159265
print(f"{pi:.2f}") # 3.14 — two decimal places
print(f"{pi:.4f}") # 3.1416
big = 1234567
print(f"{big:,}") # 1,234,567 — comma thousands separator
print(f"{big:_}") # 1_234_567 — underscores instead
ratio = 0.27
print(f"{ratio:.1%}") # 27.0% — multiplies by 100 and adds %
You can combine specifiers: ,.2f produces 1,234,567.89.
Integers in Different Bases
n = 255
print(f"{n:b}") # 11111111 — binary
print(f"{n:o}") # 377 — octal
print(f"{n:x}") # ff — hex lowercase
print(f"{n:X}") # FF — hex uppercase
print(f"{n:#x}") # 0xff — with prefix
Useful for debugging bitmasks, dumping bytes, and writing data formats.
Padding and Alignment
A number after the colon sets a minimum width. Use <, >, or ^ for left, right, and centre alignment. Pad with a custom character by putting it before the alignment marker.
name = "Ada"
print(f"[{name:<10}]") # [Ada ]
print(f"[{name:>10}]") # [ Ada]
print(f"[{name:^10}]") # [ Ada ]
print(f"[{name:*^10}]") # [***Ada****]
For numbers, the default alignment is right and the default fill is a space.
for n in [1, 10, 100, 1000]:
print(f"{n:>4}")
# 1
# 10
# 100
# 1000
You can also pad with zeros, which is the right way to write fixed-width integers.
print(f"{42:05}") # 00042
print(f"{3.14:08.3f}") # 0003.140
Sign Control
print(f"{5:+}") # +5
print(f"{-5:+}") # -5
print(f"{5: }") # " 5" — space for positive, minus for negative
Useful when you want positive and negative numbers to line up vertically.
Formatting Dates and Times
datetime objects honour strftime codes inside an f-string.
from datetime import datetime
now = datetime(2024, 6, 15, 9, 30)
print(f"{now:%Y-%m-%d}") # 2024-06-15
print(f"{now:%I:%M %p}") # 09:30 AM
print(f"{now:%A, %d %B %Y}") # Saturday, 15 June 2024
This is shorter than calling now.strftime(...) separately.
The Debug =-Syntax
A small but beloved feature: adding = after an expression prints both the source text and the value.
x = 42
y = 17
print(f"{x=}, {y=}, {x + y=}")
# x=42, y=17, x + y=59
This single trick replaces a huge number of throwaway print(f"x: {x}") statements during debugging. Combine it with a format spec.
import math
print(f"{math.pi=:.3f}") # math.pi=3.142
Nested Expressions
Anything that’s a valid Python expression can go inside the braces, including function calls and conditional expressions.
balance = -42
print(f"Balance: {'overdrawn' if balance < 0 else 'ok'}")
You can also nest formatting by using an inner f-string to compute the format specifier itself.
width = 10
print(f"{'hi':>{width}}") # right-aligned in a width set by another variable
Multi-line f-Strings
Combine an f-string with triple quotes for templates.
name = "Ada"
items = ["pen", "paper"]
print(f"""
Hi {name},
Your items:
- {items[0]}
- {items[1]}
""")
For larger templates, string.Template or a real templating library (Jinja2) tends to scale better, but for short cases the triple-quoted f-string is fine.
When Not to Use an f-String
There are a couple of cases where f-strings are not the right tool.
For logging, prefer the logger’s lazy formatting. This avoids building the string when the log level filters it out.
log.info("user %s logged in", user_id) # preferred
log.info(f"user {user_id} logged in") # builds the string even if filtered
For building SQL queries or shell commands, never use f-strings to interpolate user input — that is how SQL injection and command injection happen. Use parameterised queries or subprocess arg lists instead.
For internationalisation, where text needs to be extracted into translation files, gettext and friends rely on plain placeholder strings, not f-strings.
A Brief Comparison
name = "Ada"
age = 36
# %-formatting (oldest)
print("%s is %d" % (name, age))
# str.format
print("{} is {}".format(name, age))
# f-string (preferred)
print(f"{name} is {age}")
The f-string version is the shortest, the easiest to read, and slightly the fastest because the formatting happens at compile time.
Wrapping Up
Spend twenty minutes with the format-specifier table and f-strings cover almost every formatting need you will ever have. The combination of plain interpolation, precision control, alignment, and the = debug syntax makes them the default tool — keep %-formatting and str.format in reserve for the few cases where they still belong.
Related articles
- Python Strings in Python: Everything a Beginner Needs
A thorough introduction to Python strings — creation, escape sequences, indexing, slicing, the essential methods, f-strings, and why strings are immutable.
- Pandas Pandas String Methods Tutorial
A practical tour of the Pandas .str accessor: cleaning text, extracting patterns, splitting and joining, dealing with missing values, and writing string code that stays fast.
- Python Python asyncio Event Loop Guide
Understand how Python's asyncio event loop schedules coroutines, what await actually does, and how to avoid the classic mistakes that turn async code into a tangle of bugs.
- Python Python Decorators Deep Dive
A practical tour of Python decorators: how they work under the hood, when to use them, and how to write decorators that preserve metadata, accept arguments, and stack cleanly.