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:
# Method 1: f-stringsprint(f"Hello {name}")
# Method 2: .format()print("Hello {}".format(name))
# Method 3: % operatorprint("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:
name = "Alice"score = 95.5
# Basic usageprint(f"{name} scored {score}%")# Output: Alice scored 95.5%
# Format specifiersprint(f"{name} scored {score:.1f}%")# Output: Alice scored 95.5%
# Inline expressionsprint(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:
name = "Bob"score = 88.0
# Basic positionalprint("{} scored {:.1f}%".format(name, score))# Output: Bob scored 88.0%
# Named placeholdersprint("{name} scored {score:.1f}%".format(name="Carol", score=92.3))# Output: Carol scored 92.3%
# Reusable templatestemplate = "{} 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 accessprint("{0} and {1}".format("first", "second"))print("{1} and {0}".format("first", "second"))# Output: first and second# Output: second and firstKey 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:
name = "Dave"score = 78.5
# Basic usageprint("%s scored %.1f%%" % (name, score))# Output: Dave scored 78.5%
# Multiple values need a tupleprint("%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)ssyntax - Limited flexibility compared to newer methods
3. Performance Comparison
I ran a quick benchmark to see how fast each method is:
import timeit
# f-stringfstring_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")
# % operatorpercent_time = timeit.timeit('"%s" % x', setup='x=1', number=1000000)print(f"% operator: {percent_time:.4f}s")Results on my machine:
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:
# Logginglog_msg = f"Processing file {filename} with size {size} bytes"
# Debug outputprint(f"Variable x = {x}, y = {y}")
# Simple messagesgreeting = f"Hello, {user.name}!"
# Calculations in outputprint(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 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 databaseemail_template = "Dear {name}, your order {order_id} is ready."# Template stored somewhere, values filled in laterprint(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:
# Existing legacy code - don't change unless necessarydef 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: Mixing methods in same fileprint(f"User: {user}")print("Score: {}".format(score))print("Age: %d" % age)
# GOOD: Consistent choiceprint(f"User: {user}")print(f"Score: {score}")print(f"Age: {age}")5.2 Using % for Dict Formatting
# BAD: Clumsy dict syntax with %data = {"name": "Alice", "score": 95}print("%(name)s got %(score)d" % data)
# GOOD: Clean f-stringdata = {"name": "Alice", "score": 95}print(f"{data['name']} got {data['score']}")5.3 Using .format() When f-strings Are Simpler
# OVERCOMPLICATED: Single-use format() templateprint("Hello {}".format(name))
# SIMPLER: Just use f-stringprint(f"Hello {name}")6. Comparison Summary
Feature | f-strings | .format() | % operator-------------------- | ------------ | ------------ | ----------Syntax | f"{x}" | "{}".format(x)| "%s" % xPython version | 3.6+ | 2.6+ | AllPerformance | Fastest | Slow | SlowInline expressions | Yes | No | NoReusable templates | No | Yes | NoDebug repr (!r) | Yes | No | NoDict formatting | Clean | Clean | ClumsyRecommendation | Default | Templates | Legacy only7. 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