Python String Format
Tutorial 33 of 65 · pythondeck.com Python course
Three styles coexist: %-formatting (legacy), str.format() and f-strings (preferred, Python 3.6+). Format specs control width, alignment, precision, sign, padding and conversion. f-strings can embed expressions and = for self-debugging.
Formatting turns data into human-readable text for logs, UI, and files. Python offers f-strings (preferred), str.format(), and legacy % interpolation. Format specifications control width, alignment, precision, and type conversion—essential for tables and localised numbers.
f-strings evaluate expressions at runtime, so keep them simple; heavy logic belongs outside the string. The debug specifier f"{x=}" expands to name and value for quick diagnostics.
f-strings: f"{name} has {n} items" and expression support.
Format spec mini-language: : fill, align, sign, width, precision, type.
str.format positional, named, and {{ escaping for literals.
Legacy % formatting and when you still see it (logging, old C extensions).
format_map for dict-driven templates without unpacking.
Localization: locale.format_string or Babel for production i18n.
Alignment: > right, < left, ^ center; numeric types default to right-aligned. Use thousands separators with , inside the spec: f"{n:,}".
For reusable templates, store format strings in constants or config files and call .format() or f-strings with explicit field names to avoid ordering bugs when translators reorder phrases.
Logging uses %-style for lazy evaluation: log.info("%s", val) avoids formatting when the log level is disabled. Do not mix f-strings in logging calls if the message is expensive to build.
Putting complex expressions inside f-strings, hurting readability and testability.
Mixing format styles inconsistently within one codebase.
Using f-strings as templates for SQL or HTML (injection risk)—use parameters or escaping.
Forgetting that {{ and }} escape braces in format.
Relying on default float repr instead of explicit precision for financial display.
Prefer f-strings for new code; use named placeholders in translatable messages.
Keep format specs explicit for tables: width and alignment per column.
Use logging's %-style or logging.debug("%s", x) for deferred formatting.
Never build SQL with string formatting; use query parameters.
Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.
The program below demonstrates all three styles. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# Example: All three styles
# Run in the REPL or save as a .py file and execute with python.
name, score = "Ada", 97.3456
print("%-10s %7.2f" % (name, score))
print("{:<10} {:7.2f}".format(name, score))
print(f"{name:<10} {score:7.2f}")
This sample walks through numbers in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# Example: Numbers
# Run in the REPL or save as a .py file and execute with python.
x = 1234567
print(f"{x:,}") # 1,234,567
print(f"{0.123:.2%}") # 12.30%
print(f"{255:#x}") # 0xff
Here is a hands-on illustration of debug f-string. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.
# Example: Debug f-string
# Run in the REPL or save as a .py file and execute with python.
a, b = 3, 4
print(f"{a=}, {b=}, {a*b=}")
The program below demonstrates f-string debug. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# f-strings evaluate expressions inside { } at runtime
name, score = "Ada", 99 # sample values
print(f"{name} scored {score}") # basic interpolation
print(f"{score=}") # debug syntax prints score=99 (3.8+)
pi = 3.14159265 # float formatting
print(f"pi={pi:.3f}") # three decimal places
print(f"{10000:,}") # thousands separator
text = "deck" # alignment demo
print(f"{text:>10}") # right align width 10
This sample walks through format map in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# str.format and format_map build strings from templates
template = "{name} has {count} messages" # placeholder names
print(template.format(name="Grace", count=3)) # positional kwargs
data = {"product": "PythonDeck", "price": 19.99} # mapping
print("Buy {product} for ${price:.2f}".format_map(data)) # map lookup
parts = ["2025", "06", "04"] # sequence
print("{}-{}-{}".format(*parts)) # star-unpack sequence
legacy = "%s wins %d points" % ("Team", 42) # old % style
print(legacy) # still works but f-strings preferred