String Formatting: f-strings, format() and %
Three formatting styles exist. f-strings are the one to use, and the format specification they share controls width, alignment, precision and thousands separators.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
The three styles
name = "Meera"
score = 92.5
print("Hello %s, you scored %.1f" % (name, score)) # oldest
print("Hello {}, you scored {:.1f}".format(name, score)) # older
print(f"Hello {name}, you scored {score:.1f}") # currentAll three produce the same output. Use f-strings, available since Python 3.6. Know the other two because you will meet them in existing code, and because % style is still what the logging module expects.
f-strings
An f-string is a normal string literal prefixed with f. Anything inside braces is an expression, evaluated at that point and inserted.
name = "Meera"
age = 27
items = ["pen", "book"]
print(f"{name} is {age}")
print(f"Next year: {age + 1}") # any expression
print(f"Upper: {name.upper()}") # method calls
print(f"First item: {items[0]}") # indexing
print(f"Count: {len(items)}") # function calls
print(f"{'yes' if age >= 18 else 'no'}") # a conditional expressionThe equals sign for debugging
total = 42
rate = 0.18
print(f"{total=}") # total=42
print(f"{rate=:.2f}") # rate=0.18
print(f"{total * rate=}") # total * rate=7.56Since Python 3.8, appending = prints the expression text as well as the value. It replaces a great many throwaway print("total is", total) lines.
Braces and quotes
print(f"{{literal braces}}") # {literal braces}
data = {"name": "Meera"}
print(f"{data['name']}") # different quote style inside
# From Python 3.12 the same quote style is allowed inside as well.The format specification
After a colon inside the braces comes the format specification. It is shared by f-strings and format(), so learning it once covers both.
{value:[[fill]align][sign][#][0][width][grouping][.precision][type]}Width and alignment
word = "cat"
print(f"[{word:10}]") # [cat ] text defaults to left
print(f"[{word:<10}]") # [cat ]
print(f"[{word:>10}]") # [ cat]
print(f"[{word:^10}]") # [ cat ]
print(f"[{word:*^10}]") # [***cat****]
number = 42
print(f"[{number:10}]") # [ 42] numbers default to rightNumbers
value = 1234567.8915
print(f"{value:.2f}") # 1234567.89 two decimal places
print(f"{value:,.2f}") # 1,234,567.89 thousands separators
print(f"{value:_.2f}") # 1_234_567.89
print(f"{value:15,.2f}") # width 15, right aligned, grouped
print(f"{value:e}") # 1.234568e+06
print(f"{0.256:.1%}") # 25.6% percentage
print(f"{42:05d}") # 00042 zero padded
print(f"{42:+d}") # +42 always show the sign
print(f"{-42:+d}") # -42Other bases
n = 255
print(f"{n:b}", f"{n:o}", f"{n:x}", f"{n:X}") # 11111111 377 ff FF
print(f"{n:#x}", f"{n:#b}") # 0xff 0b11111111
print(f"{n:08b}") # 11111111 padded to 8 bitsDynamic width
width = 12
for name in ["Meera", "Arun"]:
print(f"{name:>{width}}") # the width itself comes from a variableA formatted report
rows = [
("Notebook", 3, 45.50),
("Pen", 12, 8.75),
("Highlighter set", 2, 199.00),
]
print(f"{'Item':<20}{'Qty':>5}{'Price':>12}{'Total':>12}")
print("-" * 49)
grand_total = 0
for item, quantity, price in rows:
line_total = quantity * price
grand_total += line_total
print(f"{item:<20}{quantity:>5}{price:>12,.2f}{line_total:>12,.2f}")
print("-" * 49)
print(f"{'Grand total':<37}{grand_total:>12,.2f}")Item Qty Price Total
-------------------------------------------------
Notebook 3 45.50 136.50
Pen 12 8.75 105.00
Highlighter set 2 199.00 398.00
-------------------------------------------------
Grand total 639.50str.format
print("{} scored {}".format("Meera", 92))
print("{0} scored {1}, and {0} passed".format("Meera", 92)) # by position
print("{name} scored {score}".format(name="Meera", score=92)) # by name
record = {"name": "Meera", "score": 92}
print("{name} scored {score}".format(**record))format remains useful for a template stored in a variable, where an f-string cannot be used because there is nothing to interpolate at definition time.
Percent formatting
print("%s scored %d out of %d" % ("Meera", 92, 100))
print("%.2f%%" % 25.6) # 25.60% - %% is a literal percent sign
import logging
logging.warning("User %s failed %d times", "meera", 3) # keep this style hereThe logging module defers formatting until it knows the message will actually be emitted, so passing arguments separately, in % style, is measurably better there.
Choosing between them
| Situation | Use |
|---|---|
| Normal code | f-string |
| Template stored in a variable or file | str.format |
| Logging calls | % style with separate arguments |
| Building SQL or shell commands | None of them - use the tool's parameter binding |
Never build SQL by formatting user input into a query string. That is SQL injection. Use parameter placeholders, which the security notes cover in detail.
Common mistakes
- Forgetting the
fprefix, so the braces print literally. - Writing
{value:2f}instead of{value:.2f}. Without the dot it is a width, not a precision. - Assuming
{:.2f}rounds for storage. It formats for display only. - Using the same quote character inside an f-string on Python versions before 3.12.
- Mixing
%andformatstyles in one project. - Formatting user data into a query or a command.
Best practices
- Use f-strings everywhere except logging and stored templates.
- Use
{x=}for debug output; it is shorter and it never mislabels a value. - Use
,grouping for any number a person will read. - Keep expressions inside f-strings short. Compute first, format second.
Practice
- Print 1234567.891 as
1,234,567.89, as1.23e+06and as a right aligned field of width 20. - Print a three column table of five records with aligned headings and a total row.
- Show 0.0725 as
7.25%. - Explain why
f"{name}"and"{}".format(name)both work, and give a case where only the second is possible. - Print the numbers 1 to 16 in binary, each padded to four digits, four per line.
Conclusion
Use f-strings. Learn the specification after the colon once - fill, align, width, grouping, precision, type - and it works identically in format(). Keep % style for logging, and never format untrusted input into a query.