---
title: Prefer f-strings Over % and str.format()
impact: MEDIUM
impactDescription: f-strings introduced in Python 3.6 are more readable, faster at runtime, and catch errors at parse time rather than producing cryptic runtime errors from mismatched argument counts.
tags: python, fstrings, readability, performance, quality, python3
---

## Prefer f-strings Over % and str.format()

Python 3.6 introduced f-strings (`f"..."`) as the modern, preferred way to embed expressions in string literals. They are:
- **More readable** — expression and string are co-located
- **Faster** — evaluated at the bytecode level without function dispatch
- **Safer** — syntax errors caught at parse time; no mismatched `%s` args
- **More powerful** — support arbitrary expressions and format specs inline

**Incorrect:**
```python
name = "Alice"
score = 98.567
items = ["apple", "banana"]

# %-formatting (Python 2 style) — argument count mismatches cause runtime TypeError
msg = "Hello, %s! Your score is %.2f" % (name, score)
debug = "Processing %d items: %s" % len(items)    # runtime error: int not iterable

# str.format() — verbose and error-prone with positional indices
msg = "Hello, {}! Your score is {:.2f}".format(name, score)
debug = "User {0} has {1} points ({0} is great)".format(name, score)
```

**Correct:**
```python
name = "Alice"
score = 98.567
user_id = 1042

# f-strings — expression evaluated inline
msg   = f"Hello, {name}! Your score is {score:.2f}"
debug = f"Processing {len(items)} items: {items}"

# Supports format specs
padded  = f"{user_id:05d}"          # "01042"
percent = f"{score / 100:.1%}"      # "98.6%"
repr_v  = f"Object: {obj!r}"        # calls repr()

# Multi-line f-string
report = (
    f"User: {name}\n"
    f"Score: {score:.2f}\n"
    f"Rank: {get_rank(score)}"
)
```

**Exception — logging calls should NOT use f-strings:**
```python
# In logging, keep lazy % formatting for performance (see P009)
logger.debug("Processing user %s", user_id)          # correct
logger.debug(f"Processing user {user_id}")           # incorrect — always evaluates
```

**Tools:** Ruff `UP031` (printf-string-formatting), `UP032` (f-string), `G004` (logging-f-string), pyupgrade, flynt
