Site

Formatting — Python String Formatting

Tutorial S9  •  Python / Learn

S9.0 What This Teaches

This tutorial covers string formatting in Python:

S9.1 f-strings

f-strings (formatted string literals, Python 3.6+) embed arbitrary expressions inside {}. A colon introduces a format specification:
# Formatting - f-string basics.

name = "Alice"
score = 0.9875
price = 12345.678

print(f"Name: {name}")
print(f"Score: {score:.1%}")     # 98.8%
print(f"Score: {score:.4f}")     # 0.9875
print(f"Price: ${price:,.2f}")   # $12,345.68

# Expressions inside f-strings
items = [3, 1, 4, 1, 5]
print(f"Sum: {sum(items)}, Max: {max(items)}")

# Python 3.8+ debugging shorthand: name=
print(f"{score=:.2f}")   # score=0.99

S9.2 Format Specification Mini-Language

The format spec after the colon follows the pattern [[fill]align][sign][#][0][width][grouping][.precision][type]:
n = 1234567
d = 12345.6789

# type: d=integer, f=fixed, e=scientific, g=general, x=hex, b=binary, %=percent
print(f"{n:d}")       # 1234567
print(f"{n:,}")       # 1,234,567  (thousands separator)
print(f"{n:_}")       # 1_234_567  (underscore separator)
print(f"{d:.2f}")     # 12345.68
print(f"{d:.2e}")     # 1.23e+04
print(f"{n:x}")       # 12d687  (hex lowercase)
print(f"{n:08x}")     # 0012d687  (zero-padded)
print(f"{n:#010x}")   # 0x0012d687  (with 0x prefix)
print(f"{0.1234:.1%}")  # 12.3%

# alignment: < left, > right, ^ center
print(f"{'hi':>10}")    # right-align in 10 chars
print(f"{'hi':<10}|")   # left-align
print(f"{'hi':^10}")    # center
print(f"{'hi':*^10}")   # center with * fill

S9.3 str.format()

str.format() uses named or positional placeholders. It is useful when a format string is stored in a variable or configuration:
template = "{name:<12} {score:6.1f}%"
print(template.format(name="Alice", score=98.75))
print(template.format(name="Bob",   score=72.3))

# Positional placeholders
print("{0} + {1} = {2}".format(3, 4, 7))

# Same placeholder reused
print("{0} * {0} = {1}".format(7, 49))

# Accessing attributes and items
point = (3.5, 7.1)
print("x={0[0]:.1f}, y={0[1]:.1f}".format(point))

S9.4 printf-Style % Formatting

The % operator is the oldest format mechanism, inherited from C's printf. It is common in legacy code and logging:
name = "Carol"
score = 87.5
count = 42

print("Name: %s, Score: %.1f%%" % (name, score))
print("Count: %05d" % count)       # 00042
print("Hex:   %08x" % 255)         # 000000ff

# Named placeholders with a dict
print("%(name)s scored %(score).1f%%" % {"name": name, "score": score})
Prefer f-strings for new code. Use % only when interfacing with logging (the logging module defers % formatting until the message is actually emitted, which saves work at high log levels).

S9.5 Locale and Decimal Formatting

import locale
from decimal import Decimal, ROUND_HALF_UP

# locale - platform-dependent currency and number formatting
locale.setlocale(locale.LC_ALL, '')          # use system default
print(locale.currency(12345.67))             # $12,345.67 (on US system)
print(locale.format_string("%.2f", 1234567.89, grouping=True))

# decimal.Decimal for exact financial arithmetic (no float rounding errors)
price = Decimal("19.99")
tax   = Decimal("0.0825")
total = (price * (1 + tax)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print(f"Total: ${total}")   # Total: $21.64

# Decimal format spec works like float
print(f"{total:.4f}")   # 21.6450

S9.6 Making Types Formattable

Implement __format__ on a class so that f-strings and format() can apply format specs to instances:
class Temperature:
    def __init__(self, celsius: float):
        self.c = celsius

    def __format__(self, spec: str) -> str:
        if spec == "F":
            return f"{self.c * 9/5 + 32:.1f}°F"
        if spec == "K":
            return f"{self.c + 273.15:.2f}K"
        return f"{self.c:.1f}°C"

t = Temperature(100)
print(f"{t}")    # 100.0°C
print(f"{t:F}")  # 212.0°F
print(f"{t:K}")  # 373.15K

S9.7 Example - All Together

# Formatting - tabular sales report with f-string alignment.

sales = [
    ("North",  1500, 74250.0),
    ("South",   820, 41000.0),
    ("East",   2300, 115000.0),
    ("West",   1100, 55500.0),
]

header = f"{'Region':<8} {'Units':>8} {'Revenue':>14} {'Avg/Unit':>12}"
print(header)
print("-" * len(header))

for region, units, revenue in sales:
    avg = revenue / units
    print(f"{region:<8} {units:>8,} {revenue:>14,.2f} {avg:>12,.2f}")

total_units = sum(u for _, u, _ in sales)
total_rev   = sum(r for _, _, r in sales)
print("-" * len(header))
print(f"{'Total':<8} {total_units:>8,} {total_rev:>14,.2f}")
Expected output:
Region      Units        Revenue     Avg/Unit
--------------------------------------------
North       1,500      74,250.00        49.50
South         820      41,000.00        50.00
East        2,300     115,000.00        50.00
West        1,100      55,500.00        50.45
--------------------------------------------
Total       5,720     285,750.00

S9.8 Exercise

Exercise
  • Format the number 9876543.21 five ways: with underscore grouping, as a percentage of 10,000,000, in hex with 0x prefix, in scientific notation with 3 significant figures, and right-aligned in a 20-character field.
  • Build a reusable template string (for use with .format()) that prints a two-column table of names and scores. Print five rows using the same template.
  • Implement __format__ on a Vector2D class that accepts "polar" for (r, θ) and defaults to (x, y). Use it in an f-string.

S9.9 Common Mistakes

Confusing % with format spec type for percent

ratio = 0.875
print(f"{ratio}%")      # 0.875%  - just appends %, no conversion
print(f"{ratio:.1%}")   # 87.5%   - multiplies by 100 then formats

Using float for money calculations

price = 1.10 + 2.20
print(price)            # 3.3000000000000003  (float rounding error)

from decimal import Decimal
price = Decimal("1.10") + Decimal("2.20")
print(price)            # 3.30  (exact)

f-string = shorthand in production output

x = 42
print(f"{x=}")   # x=42 - great for debugging, not for user-facing output
print(f"x = {x}")  # x = 42 - use explicit label for production

S9.10 Key Terms

TermMeaning
f-stringf"..." literal; evaluates expressions and applies format specs inline
format specMini-language after colon: [[fill]align][sign][#][0][width][group][.prec][type]
str.format()Format method using named or positional {} placeholders
% formattingLegacy printf-style formatting; common in logging
DecimalFixed-precision decimal type; avoids float rounding errors
localeStandard library module for locale-aware number and currency formatting
__format__Dunder method making a class respond to format specs in f-strings
{x=}Python 3.8+ debug shorthand; prints "x=value" without extra code