S9.0 What This Teaches
- f-strings: embedding expressions with format specs
- The format specification mini-language: fill, align, width, precision, type
str.format()for reusable format strings- printf-style
%formatting (legacy but common) - Locale-aware formatting with
localeanddecimal - Making custom types formattable with
__format__
S9.1 f-strings
{}. 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
[[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
% 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})
% 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
__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}")
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
0xprefix, 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 aVector2Dclass 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
| Term | Meaning |
|---|---|
| f-string | f"..." literal; evaluates expressions and applies format specs inline |
| format spec | Mini-language after colon: [[fill]align][sign][#][0][width][group][.prec][type] |
| str.format() | Format method using named or positional {} placeholders |
| % formatting | Legacy printf-style formatting; common in logging |
| Decimal | Fixed-precision decimal type; avoids float rounding errors |
| locale | Standard 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 |