Python has three ways to format strings. Here's when to use each.
f-strings (Recommended)
The modern way. Available since Python 3.6.
name = "Owen"
age = 25
# Basic
message = f"Hello, {name}!"
# Expressions
message = f"In 10 years: {age + 10}"
# Method calls
message = f"Name: {name.upper()}"
# Format specifiers
price = 19.99
message = f"Price: ${price:.2f}"Format Specifiers
# Numbers
f"{42:05d}" # "00042" (zero-padded)
f"{42:>5d}" # " 42" (right-aligned)
f"{42:<5d}" # "42 " (left-aligned)
f"{42:^5d}" # " 42 " (centered)
# Floats
f"{3.14159:.2f}" # "3.14"
f"{3.14159:.0f}" # "3"
f"{1234.5:,.2f}" # "1,234.50"
f"{0.25:.0%}" # "25%"
# Scientific
f"{1234567:.2e}" # "1.23e+06"Debug Mode
x = 42
print(f"\{x=\}") # "x=42"
name = "Owen"
print(f"\{name=\}") # "name='Owen'"str.format()
Older but still useful.
# Positional
"Hello, {}!".format("Owen")
# Named
"Hello, {name}!".format(name="Owen")
# Indexed
"{0} + {1} = {2}".format(1, 2, 3)
# From dict
data = {"name": "Owen", "age": 25}
"Name: {name}, Age: {age}".format(**data)When format() Is Better
# Template stored separately
template = "Hello, {name}!"
message = template.format(name="Owen")
# Reusing placeholders
"{0} {1} {0}".format("a", "b") # "a b a"% Formatting (Legacy)
Old C-style. Still works but avoid in new code.
# Basic
"Hello, %s!" % "Owen"
# Multiple values
"Name: %s, Age: %d" % ("Owen", 25)
# Floats
"Price: %.2f" % 19.99
# Dict
"%(name)s is %(age)d" % {"name": "Owen", "age": 25}Common Specifiers
%s # String
%d # Integer
%f # Float
%x # Hex
%o # Octal
%% # Literal %Comparison
name = "Owen"
age = 25
# f-string (best)
f"Name: {name}, Age: {age}"
# format()
"Name: {}, Age: {}".format(name, age)
# % formatting (avoid)
"Name: %s, Age: %d" % (name, age)Template Strings
For untrusted input (user-provided templates):
from string import Template
# Safe - won't execute code
t = Template("Hello, $name!")
t.substitute(name="Owen")
# Missing key raises
t.safe_substitute(name="Owen") # Returns partial resultUse templates when the format string comes from users.
Multiline Strings
# f-string
message = f"""
Name: {name}
Age: {age}
Status: Active
"""
# Cleaner with parentheses
message = (
f"Name: {name}\n"
f"Age: {age}\n"
f"Status: Active"
)Performance
f-strings are fastest:
# Fastest
f"{name} is {age}"
# Slower
"{} is {}".format(name, age)
# Slowest
"%s is %d" % (name, age)Difference is small but f-strings win.
My Rules
- Use f-strings for new code
- Use format() for templates stored as data
- Use Template for user-provided format strings
- Avoid % in new code
- Use {x=} for debugging
f-strings are the answer 95% of the time.
React to this post: