Skip to content

Which Python String Formatting Method Should You Use: f-strings, format(), or %?

1. Purpose

I was reading through some Python code examples and noticed three different ways to format strings:

mixed_formatting.py
# Method 1: f-strings
print(f"Hello {name}")
# Method 2: .format()
print("Hello {}".format(name))
# Method 3: % operator
print("Hello %s" % name)

Which one should I use? I had no idea. Some older tutorials used %, some used .format(), and modern code seemed to prefer f-strings. I needed to understand the differences and make an informed choice.

2. The Three Methods Explained

2.1 f-strings (Formatted String Literals)

f-strings were introduced in Python 3.6. They allow you to embed expressions directly inside string literals using {} braces:

fstrings_demo.py
name = "Alice"
score = 95.5
# Basic usage
print(f"{name} scored {score}%")
# Output: Alice scored 95.5%
# Format specifiers
print(f"{name} scored {score:.1f}%")
# Output: Alice scored 95.5%
# Inline expressions
print(f"Next year: {2024 + 1}")
# Output: Next year: 2025
# Debugging with !r (repr)
print(f"Debug: {name!r}")
# Output: Debug: 'Alice'

Key features:

  • Expressions evaluated at runtime
  • Most readable syntax
  • Fastest performance
  • Format specifiers like .1f, :>10, :, work inside braces

2.2 The .format() Method

The .format() method was introduced in Python 2.6/3.0. It uses {} placeholders in a template string:

format_demo.py
name = "Bob"
score = 88.0
# Basic positional
print("{} scored {:.1f}%".format(name, score))
# Output: Bob scored 88.0%
# Named placeholders
print("{name} scored {score:.1f}%".format(name="Carol", score=92.3))
# Output: Carol scored 92.3%
# Reusable templates
template = "{} scored {:.1f}%"
print(template.format("Bob", 88.0))
print(template.format("Carol", 92.3))
# Output: Bob scored 88.0%
# Output: Carol scored 92.3%
# Index-based access
print("{0} and {1}".format("first", "second"))
print("{1} and {0}".format("first", "second"))
# Output: first and second
# Output: second and first

Key features:

  • Reusable templates (define once, format multiple times)
  • Lazy evaluation (format happens when you call .format())
  • Works in Python 2.6+ for compatibility
  • Supports indexing and named placeholders

2.3 The % Operator (Old-Style Formatting)

The % operator comes from C-style printf formatting. It’s been in Python since the beginning:

percent_demo.py
name = "Dave"
score = 78.5
# Basic usage
print("%s scored %.1f%%" % (name, score))
# Output: Dave scored 78.5%
# Multiple values need a tuple
print("%s is %d years old" % (name, 25))
# Dict formatting (clumsy)
data = {"name": "Eve", "score": 85}
print("%(name)s scored %(score).1f%%" % data)
# Output: Eve scored 85.0%

Key features:

  • C-style syntax familiar to C/C++ programmers
  • Works in all Python versions
  • Dict formatting requires special %(key)s syntax
  • Limited flexibility compared to newer methods

3. Performance Comparison

I ran a quick benchmark to see how fast each method is:

benchmark.py
import timeit
# f-string
fstring_time = timeit.timeit('f"{x}"', setup='x=1', number=1000000)
print(f"f-string: {fstring_time:.4f}s")
# .format()
format_time = timeit.timeit('"{}".format(x)', setup='x=1', number=1000000)
print(f".format(): {format_time:.4f}s")
# % operator
percent_time = timeit.timeit('"%s" % x', setup='x=1', number=1000000)
print(f"% operator: {percent_time:.4f}s")

Results on my machine:

benchmark_results.txt
f-string: 0.0528s (fastest)
.format(): 0.1476s (~3x slower)
% operator: 0.1215s (~2x slower)

f-strings are significantly faster because they’re evaluated at compile time and don’t require a method call overhead.

4. When to Use Each Method

4.1 Use f-strings (Default Choice)

Use f-strings for 90% of cases:

good_fstring.py
# Logging
log_msg = f"Processing file {filename} with size {size} bytes"
# Debug output
print(f"Variable x = {x}, y = {y}")
# Simple messages
greeting = f"Hello, {user.name}!"
# Calculations in output
print(f"Total: {sum(values):,} items")

Why: Fastest, most readable, supports inline expressions.

4.2 Use .format() When You Need Templates

Use .format() when you need reusable templates or delayed formatting:

template_example.py
# Template for logging (define once, use many times)
LOG_TEMPLATE = "[{timestamp}] {level}: {message}"
def log(level, message):
timestamp = datetime.now().isoformat()
print(LOG_TEMPLATE.format(timestamp=timestamp, level=level, message=message))
# Template from config file or database
email_template = "Dear {name}, your order {order_id} is ready."
# Template stored somewhere, values filled in later
print(email_template.format(name="Alice", order_id="12345"))
# Compatibility with older Python (< 3.6)
def legacy_code(value):
return "Value: {}".format(value) # Works in Python 2.6+

Why: Reusable, lazy evaluation, Python 2.6+ compatibility.

4.3 Use % Operator Only for Legacy Code

Only use % when maintaining existing code:

legacy_percent.py
# Existing legacy code - don't change unless necessary
def old_logging_module(msg, level):
return "[%s] %s" % (level, msg) # Keep as-is in legacy code
# If you need C-style printf familiarity for a team
# (but still consider switching to f-strings)

Why: Legacy compatibility only. New code should avoid it.

5. Common Mistakes

5.1 Mixing Methods Inconsistently

bad_mixed.py
# BAD: Mixing methods in same file
print(f"User: {user}")
print("Score: {}".format(score))
print("Age: %d" % age)
# GOOD: Consistent choice
print(f"User: {user}")
print(f"Score: {score}")
print(f"Age: {age}")

5.2 Using % for Dict Formatting

bad_dict_format.py
# BAD: Clumsy dict syntax with %
data = {"name": "Alice", "score": 95}
print("%(name)s got %(score)d" % data)
# GOOD: Clean f-string
data = {"name": "Alice", "score": 95}
print(f"{data['name']} got {data['score']}")

5.3 Using .format() When f-strings Are Simpler

overcomplicated.py
# OVERCOMPLICATED: Single-use format() template
print("Hello {}".format(name))
# SIMPLER: Just use f-string
print(f"Hello {name}")

6. Comparison Summary

comparison_table.txt
Feature | f-strings | .format() | % operator
-------------------- | ------------ | ------------ | ----------
Syntax | f"{x}" | "{}".format(x)| "%s" % x
Python version | 3.6+ | 2.6+ | All
Performance | Fastest | Slow | Slow
Inline expressions | Yes | No | No
Reusable templates | No | Yes | No
Debug repr (!r) | Yes | No | No
Dict formatting | Clean | Clean | Clumsy
Recommendation | Default | Templates | Legacy only

7. Summary

I was confused by seeing three different string formatting methods in Python code. After investigating:

  • f-strings (f"{x}"): Use for most cases—fastest, most readable, supports inline expressions
  • .format(): Use when you need reusable templates, lazy evaluation, or Python 2.6+ compatibility
  • % operator: Use only for legacy code maintenance—it’s older and less flexible

The Python 3 tutorial explicitly marks % formatting as “旧式字符串格式化方法” (old-style string formatting), and presents f-strings first in the documentation—indicating it’s the recommended approach.

Default to f-strings. Use .format() for templates. Avoid % in new code.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments