String formatting lets Python programs combine values with readable text while controlling alignment, precision, numeric separators, dates, and other presentation details. Modern Python generally favors f-strings for application code.

1. F-Strings

Available since Python 3.6, f-strings embed expressions directly:

name = "Alice"
age = 30

message = f"{name} is {age} years old."
print(message)

Expressions can be evaluated inside the braces:

width = 5
height = 3
print(f"Area: {width * height}")

Keep embedded expressions reasonably small. Complex logic is easier to read when calculated before the f-string.

2. Format Numeric Precision

price = 19.9876
print(f"{price:.2f}")  # 19.99

Percentages:

ratio = 0.8734
print(f"{ratio:.1%}")  # 87.3%

Thousands separators:

population = 1_234_567
print(f"{population:,}")

3. Alignment and Width

Left-align, right-align, or center text:

print(f"{'Name':<10} {'Age':>5}")
print(f"{'Alice':<10} {25:>5}")
print(f"{'Bob':<10} {30:>5}")

Center a value:

print(f"{'Python':^20}")

Use a fill character:

print(f"{'Title':-^20}")

4. Integer Bases

number = 42

print(f"binary: {number:b}")
print(f"octal: {number:o}")
print(f"hex: {number:x}")

Prefixes can be included with #:

print(f"{number:#x}")  # 0x2a

5. Debugging with =

Python 3.8+ supports a convenient debugging form:

name = "Alice"
age = 30
print(f"{name=}, {age=}")

This prints both the expression and its representation.

6. !s and !r

Choose string or representation conversion explicitly:

value = "hello\nworld"

print(f"{value!s}")
print(f"{value!r}")

!r is particularly useful for debugging because escape characters and quotes remain visible.

7. str.format()

Older code frequently uses str.format():

message = "{} is {} years old.".format("Alice", 30)

Named fields can improve clarity:

message = "{name} is {age} years old.".format(name="Alice", age=30)

str.format() remains supported and can be useful when the format string comes from a source that should not contain arbitrary Python expressions.

8. Old % Formatting

Legacy Python code may contain:

name = "Alice"
age = 30
print("%s is %d years old." % (name, age))

This style is still used by some APIs, notably the standard logging module, where deferred % formatting is intentional:

import logging

logging.info("User %s logged in", name)

For ordinary new string construction, f-strings are usually clearer.

9. Dates and Times

datetime objects support format specifications:

from datetime import datetime

now = datetime(2026, 9, 1, 10, 30)
print(f"{now:%Y-%m-%d %H:%M}")

10. Do Not Build SQL with String Formatting

Avoid constructing SQL queries with f-strings or other formatting methods using untrusted values:

# Avoid this:
# query = f"SELECT * FROM users WHERE name = '{name}'"

Use the parameter mechanism provided by the database driver instead. The same principle applies to shell commands and other contexts where quoting and injection matter.

Conclusion

F-strings are the best default for most modern Python formatting. Learn the format-specification mini-language for precision, alignment, and numeric presentation, keep complex logic outside the string, and use specialized parameter APIs instead of formatting untrusted data into SQL or shell commands.