String Processing
String Formatting
Building dynamic output strings is essential for user messages, reports, and data display. Python provides multiple formatting approaches: legacy %-formatting, the str.format() method, and modern f-strings. Understanding all three helps you read existing code and choose the best tool for each situation.
Old-style formatting (%)
The original C-style formatting using the % operator.
# Old-style % formatting
name = "Alice"
age = 30
height = 5.8
# Basic formatting
print("Name: %s" % name)
print("Age: %d" % age)
print("Height: %.1f" % height)
# Multiple values (tuple)
print("Name: %s, Age: %d" % (name, age))
# Format specifiers
print("Integer: %d" % 42)
print("Float: %f" % 3.14159)
print("Scientific: %e" % 1000000)
print("Percentage: %.2f%%" % 95.5)
# Width and precision
print("Padded: %10s" % "test")
print("Number: %05d" % 42)
print("Decimal: %8.2f" % 3.14159)
name ← Alice, age ← 30, height ← 5.8
3name→ Alice = "Alice"4age→ 30 = 305height→ 5.8 = 5.867# Basic formatting8print("Name: %s" % nameAlice)9print("Age: %d" % age30)10print("Height: %.1f" % height5.8)1112# Multiple values (tuple)13print("Name: %s, Age: %d" % (nameAlice, age30))1415# Format specifiers16print("Integer: %d" % 42)17print("Float: %f" % 3.14159)18print("Scientific: %e" % 1000000)19print("Percentage: %.2f%%" % 95.5)2021# Width and precision22print("Padded: %10s" % "test")23print("Number: %05d" % 42)24print("Decimal: %8.2f" % 3.14159)outputName: Alice Age: 30 Height: 5.8 Name: Alice, Age: 30 Integer: 42 Float: 3.141590 Scientific: 1.000000e+06 Percentage: 95.50% Padded: test Number: 00042 Decimal: 3.14
Use %s for strings, %d for integers, %f for floats. Pass values as tuple.
str.format() method
Flexible formatting with positional and named placeholders.
# str.format() method
name = "Bob"
age = 25
balance = 1234.56
# Positional arguments
print("Name: {}, Age: {}".format(name, age))
# Indexed arguments
print("Age: {1}, Name: {0}".format(name, age))
# Named arguments
print("Name: {n}, Age: {a}".format(n=name, a=age))
# Mixed
print("{0} is {1} years old. {0} has ${2:.2f}".format(name, age, balance))
# From dictionary
data = {'name': 'Charlie', 'age': 35}
print("Name: {name}, Age: {age}".format(**data))
# Attribute access
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("David", 40)
print("Name: {p.name}, Age: {p.age}".format(p=person))
name ← Bob, age ← 25, balance ← 1234.56, data ← {'name': 'Charlie', 'age': 35}
3name→ Bob = "Bob"4age→ 25 = 255balance→ 1234.56 = 1234.5667# Positional arguments8print("Name: {}, Age: {}".format(nameBob, age25))910# Indexed arguments11print("Age: {1}, Name: {0}".format(nameBob, age25))1213# Named arguments14print("Name: {n}, Age: {a}".format(n=nameBob, a=age25))1516# Mixed17print("{0} is {1} years old. {0} has ${2:.2f}".format(nameBob, age25, balance1234.56))1819# From dictionary20data→ {'name': 'Charlie', 'age': 35} = {'name': 'Charlie', 'age': 35}21print("Name: {name}, Age: {age}".format(**data{'name': 'Charlie', 'age': 35}))2223# Attribute access24class Person:25 def __init__(self, name, age):26 self.name = name27 self.age = age2829person = Person("David", 40)30print("Name: {p.name}, Age: {p.age}".format(p=person))outputName: Bob, Age: 25 Age: 25, Name: Bob Name: Bob, Age: 25 Bob is 25 years old. Bob has $1234.56 Name: Charlie, Age: 35self.name ← David, self.age ← 40
24class Person:25 def __init__(self⟨Person A⟩, nameDavid, age40):26 self.name→ David = nameDavid27 self.age→ 40 = age40person ← ⟨Person A⟩
29person→ ⟨Person A⟩ = Person("David", 40)30print("Name: {p.name}, Age: {p.age}".format(p=person⟨Person A⟩))outputName: David, Age: 40
Use {} placeholders with .format(). Supports indexing and named arguments.
F-strings (modern)
Inline expressions directly in string literals using f-prefix.
# F-strings (formatted string literals)
name = "Eve"
age = 28
balance = 5432.10
# Basic f-string
print(f"Name: {name}, Age: {age}")
# Expressions inside f-strings
print(f"Next year {name} will be {age + 1}")
print(f"Double balance: ${balance * 2:.2f}")
# Method calls
text = "hello world"
print(f"Uppercase: {text.upper()}")
print(f"Title case: {text.title()}")
# Calculations
x, y = 10, 20
print(f"{x} + {y} = {x + y}")
print(f"{x} * {y} = {x * y}")
# Multiple lines
message = (
f"User Profile:\n"
f" Name: {name}\n"
f" Age: {age}\n"
f" Balance: ${balance:.2f}"
)
print(message)
name ← Eve, age ← 28, balance ← 5432.1, text ← hello world, x ← 10
3name→ Eve = "Eve"4age→ 28 = 285balance→ 5432.1 = 5432.1067# Basic f-string8print(f"Name: {nameEve}, Age: {age28}")910# Expressions inside f-strings11print(f"Next year {nameEve} will be {age28 + 1}")12print(f"Double balance: ${balance5432.1 * 2:.2f}")1314# Method calls15text→ hello world = "hello world"16print(f"Uppercase: {texthello world.upper()}")17print(f"Title case: {texthello world.title()}")1819# Calculations20x→ 10, y→ 20 = 10, 2021print(f"{x10} + {y20} = {x + y}")22print(f"{x10} * {y20} = {x * y}")2324# Multiple lines25message→ User Profile: Name: Eve Age: 28 Balance: $5432.10 = (26 f"User Profile:\n"27 f" Name: {nameEve}\n"28 f" Age: {age28}\n"29 f" Balance: ${balance5432.1:.2f}"30)31print(messageUser Profile: Name: Eve Age: 28 Balance: $5432.10)outputName: Eve, Age: 28 Next year Eve will be 29 Double balance: $10864.20 Uppercase: HELLO WORLD Title case: Hello World 10 + 20 = 30 10 * 20 = 200 User Profile: Name: Eve Age: 28 Balance: $5432.10
Prefix string with f, embed expressions in {braces}. Most readable approach.
Number formatting
Format specifiers for decimal places, separators, and number bases.
# Number formatting with f-strings
pi = 3.14159265359
num = 1234567
# Decimal places
print(f"Pi: {pi:.2f}") # 2 decimals
print(f"Pi: {pi:.4f}") # 4 decimals
# Width and alignment
print(f"Pi: {pi:10.2f}") # Width 10, 2 decimals
print(f"Pi: {pi:012.2f}") # Zero-padded
# Thousands separator
print(f"Number: {num:,}")
print(f"Number: {num:_}") # Underscore separator
# Percentage
ratio = 0.756
print(f"Ratio: {ratio:.1%}") # 75.6%
print(f"Ratio: {ratio:.2%}") # 75.60%
# Scientific notation
big_num = 1234567890
print(f"Scientific: {big_num:e}")
print(f"Scientific: {big_num:.2e}")
# Binary, octal, hex
value = 42
print(f"Binary: {value:b}")
print(f"Octal: {value:o}")
print(f"Hex: {value:x}")
print(f"Hex (uppercase): {value:X}")
# Number formatting with f-strings
pi = 3.14159265359
num = 1000
# Decimal places
print(f"Pi: {pi:.2f}") # 2 decimals
print(f"Pi: {pi:.4f}") # 4 decimals
# Width and alignment
print(f"Pi: {pi:10.2f}") # Width 10, 2 decimals
print(f"Pi: {pi:012.2f}") # Zero-padded
# Thousands separator
print(f"Number: {num:,}")
print(f"Number: {num:_}") # Underscore separator
# Percentage
ratio = 0.756
print(f"Ratio: {ratio:.1%}") # 75.6%
print(f"Ratio: {ratio:.2%}") # 75.60%
# Scientific notation
big_num = 1234567890
print(f"Scientific: {big_num:e}")
print(f"Scientific: {big_num:.2e}")
# Binary, octal, hex
value = 42
print(f"Binary: {value:b}")
print(f"Octal: {value:o}")
print(f"Hex: {value:x}")
print(f"Hex (uppercase): {value:X}")
# Number formatting with f-strings
pi = 3.14159265359
num = 987654321
# Decimal places
print(f"Pi: {pi:.2f}") # 2 decimals
print(f"Pi: {pi:.4f}") # 4 decimals
# Width and alignment
print(f"Pi: {pi:10.2f}") # Width 10, 2 decimals
print(f"Pi: {pi:012.2f}") # Zero-padded
# Thousands separator
print(f"Number: {num:,}")
print(f"Number: {num:_}") # Underscore separator
# Percentage
ratio = 0.756
print(f"Ratio: {ratio:.1%}") # 75.6%
print(f"Ratio: {ratio:.2%}") # 75.60%
# Scientific notation
big_num = 1234567890
print(f"Scientific: {big_num:e}")
print(f"Scientific: {big_num:.2e}")
# Binary, octal, hex
value = 42
print(f"Binary: {value:b}")
print(f"Octal: {value:o}")
print(f"Hex: {value:x}")
print(f"Hex (uppercase): {value:X}")
pi ← 3.14159265359, num ← 1234567, ratio ← 0.756, big_num ← 1234567890
3pi→ 3.14159265359 = 3.141592653594num→ 1234567 = 12345675#@num=1000, 98765432167# Decimal places8print(f"Pi: {pi3.14159265359:.2f}") # 2 decimals9print(f"Pi: {pi3.14159265359:.4f}") # 4 decimals1011# Width and alignment12print(f"Pi: {pi3.14159265359:10.2f}") # Width 10, 2 decimals13print(f"Pi: {pi3.14159265359:012.2f}") # Zero-padded1415# Thousands separator16print(f"Number: {num1234567:,}")17print(f"Number: {num1234567:_}") # Underscore separator1819# Percentage20ratio→ 0.756 = 0.75621print(f"Ratio: {ratio0.756:.1%}") # 75.6%22print(f"Ratio: {ratio0.756:.2%}") # 75.60%2324# Scientific notation25big_num→ 1234567890 = 123456789026print(f"Scientific: {big_num1234567890:e}")27print(f"Scientific: {big_num1234567890:.2e}")2829# Binary, octal, hex30value→ 42 = 4231print(f"Binary: {value42:b}")32print(f"Octal: {value42:o}")33print(f"Hex: {value42:x}")34print(f"Hex (uppercase): {value42:X}")outputPi: 3.14 Pi: 3.1416 Pi: 3.14 Pi: 000000003.14 Number: 1,234,567 Number: 1_234_567 Ratio: 75.6% Ratio: 75.60% Scientific: 1.234568e+09 Scientific: 1.23e+09 Binary: 101010 Octal: 52 Hex: 2a Hex (uppercase): 2A
pi ← 3.14159265359, num ← 1000, ratio ← 0.756, big_num ← 1234567890
3pi→ 3.14159265359 = 3.141592653594num→ 1000 = 100056# Decimal places7print(f"Pi: {pi3.14159265359:.2f}") # 2 decimals8print(f"Pi: {pi3.14159265359:.4f}") # 4 decimals910# Width and alignment11print(f"Pi: {pi3.14159265359:10.2f}") # Width 10, 2 decimals12print(f"Pi: {pi3.14159265359:012.2f}") # Zero-padded1314# Thousands separator15print(f"Number: {num1000:,}")16print(f"Number: {num1000:_}") # Underscore separator1718# Percentage19ratio→ 0.756 = 0.75620print(f"Ratio: {ratio0.756:.1%}") # 75.6%21print(f"Ratio: {ratio0.756:.2%}") # 75.60%2223# Scientific notation24big_num→ 1234567890 = 123456789025print(f"Scientific: {big_num1234567890:e}")26print(f"Scientific: {big_num1234567890:.2e}")2728# Binary, octal, hex29value→ 42 = 4230print(f"Binary: {value42:b}")31print(f"Octal: {value42:o}")32print(f"Hex: {value42:x}")33print(f"Hex (uppercase): {value42:X}")outputPi: 3.14 Pi: 3.1416 Pi: 3.14 Pi: 000000003.14 Number: 1,000 Number: 1_000 Ratio: 75.6% Ratio: 75.60% Scientific: 1.234568e+09 Scientific: 1.23e+09 Binary: 101010 Octal: 52 Hex: 2a Hex (uppercase): 2A
pi ← 3.14159265359, num ← 987654321, ratio ← 0.756, big_num ← 1234567890
3pi→ 3.14159265359 = 3.141592653594num→ 987654321 = 98765432156# Decimal places7print(f"Pi: {pi3.14159265359:.2f}") # 2 decimals8print(f"Pi: {pi3.14159265359:.4f}") # 4 decimals910# Width and alignment11print(f"Pi: {pi3.14159265359:10.2f}") # Width 10, 2 decimals12print(f"Pi: {pi3.14159265359:012.2f}") # Zero-padded1314# Thousands separator15print(f"Number: {num987654321:,}")16print(f"Number: {num987654321:_}") # Underscore separator1718# Percentage19ratio→ 0.756 = 0.75620print(f"Ratio: {ratio0.756:.1%}") # 75.6%21print(f"Ratio: {ratio0.756:.2%}") # 75.60%2223# Scientific notation24big_num→ 1234567890 = 123456789025print(f"Scientific: {big_num1234567890:e}")26print(f"Scientific: {big_num1234567890:.2e}")2728# Binary, octal, hex29value→ 42 = 4230print(f"Binary: {value42:b}")31print(f"Octal: {value42:o}")32print(f"Hex: {value42:x}")33print(f"Hex (uppercase): {value42:X}")outputPi: 3.14 Pi: 3.1416 Pi: 3.14 Pi: 000000003.14 Number: 987,654,321 Number: 987_654_321 Ratio: 75.6% Ratio: 75.60% Scientific: 1.234568e+09 Scientific: 1.23e+09 Binary: 101010 Octal: 52 Hex: 2a Hex (uppercase): 2A
Use :.2f for decimals, :, for thousands, :b/:x/:o for binary/hex/octal.
Alignment and padding
Control width, alignment, and fill characters for formatted output.
# String alignment and padding
text = "Python"
num = 42
# Left align (default for strings)
print(f"'{text:<15}'")
# Right align (default for numbers)
print(f"'{text:>15}'")
# Center align
print(f"'{text:^15}'")
# Custom fill character
print(f"'{text:*<15}'")
print(f"'{text:->15}'")
print(f"'{text:=^15}'")
# Number alignment
print(f"'{num:<10}'")
print(f"'{num:>10}'")
print(f"'{num:^10}'")
# Table formatting
print("\nTable Example:")
print(f"{'Name':<12} {'Age':>5} {'Balance':>10}")
print(f"{'-'*12} {'-'*5} {'-'*10}")
print(f"{'Alice':<12} {30:>5} {1234.56:>10.2f}")
print(f"{'Bob':<12} {25:>5} {987.65:>10.2f}")
print(f"{'Charlie':<12} {35:>5} {5432.10:>10.2f}")
text ← Python, num ← 42
3text→ Python = "Python"4num→ 42 = 4256# Left align (default for strings)7print(f"'{textPython:<15}'")89# Right align (default for numbers)10print(f"'{textPython:>15}'")1112# Center align13print(f"'{textPython:^15}'")1415# Custom fill character16print(f"'{textPython:*<15}'")17print(f"'{textPython:->15}'")18print(f"'{textPython:=^15}'")1920# Number alignment21print(f"'{num42:<10}'")22print(f"'{num42:>10}'")23print(f"'{num42:^10}'")2425# Table formatting26print("\nTable Example:")27print(f"{'Name':<12} {'Age':>5} {'Balance':>10}")28print(f"{'-'*12} {'-'*5} {'-'*10}")29print(f"{'Alice':<12} {30:>5} {1234.56:>10.2f}")30print(f"{'Bob':<12} {25:>5} {987.65:>10.2f}")31print(f"{'Charlie':<12} {35:>5} {5432.10:>10.2f}")output'Python ' ' Python' ' Python ' 'Python*********' '---------Python' '====Python=====' '42 ' ' 42' ' 42 ' Table Example: Name Age Balance ------------ ----- ---------- Alice 30 1234.56 Bob 25 987.65 Charlie 35 5432.10
Use < left, > right, ^ center. Add fill character before alignment.
Practical example
Combine formatting techniques to build formatted reports.
# Practical example: Report generation
from datetime import datetime
class Transaction:
def __init__(self, date, description, amount):
self.date = date
self.description = description
self.amount = amount
# Sample data
transactions = [
Transaction(datetime(2026, 1, 15), "Grocery Store", -45.67),
Transaction(datetime(2026, 1, 18), "Salary Deposit", 2500.00),
Transaction(datetime(2026, 1, 20), "Electric Bill", -87.50),
Transaction(datetime(2026, 1, 22), "Restaurant", -32.40),
Transaction(datetime(2026, 1, 25), "Online Purchase", -156.89),
]
# Generate report
print("=" * 70)
print(f"{'BANK STATEMENT':^70}")
print("=" * 70)
print()
print(f"{'Date':<12} {'Description':<25} {'Amount':>15}")
print("-" * 70)
balance = 1000.00
for txn in transactions:
balance += txn.amount
date_str = txn.date.strftime("%Y-%m-%d")
# Color-code positive/negative (using symbols)
sign = "+" if txn.amount >= 0 else "-"
amount_str = f"{sign}${abs(txn.amount):.2f}"
print(f"{date_str:<12} {txn.description:<25} {amount_str:>15}")
print("-" * 70)
print(f"{'FINAL BALANCE:':<37} ${balance:>15,.2f}")
print("=" * 70)
# Summary statistics
total_deposits = sum(t.amount for t in transactions if t.amount > 0)
total_withdrawals = sum(t.amount for t in transactions if t.amount < 0)
print()
print("SUMMARY")
print(f" Total Deposits: ${total_deposits:>10,.2f}")
print(f" Total Withdrawals: ${abs(total_withdrawals):>10,.2f}")
print(f" Net Change: ${total_deposits + total_withdrawals:>10,.2f}")
transactions = [
11# Sample data12transactions = [13 Transaction(datetime(2026, 1, 15), "Grocery Store", -45.67),14 Transaction(datetime(2026, 1, 18), "Salary Deposit", 2500.00),15 Transaction(datetime(2026, 1, 20), "Electric Bill", -87.50),16 Transaction(datetime(2026, 1, 22), "Restaurant", -32.40),17 Transaction(datetime(2026, 1, 25), "Online Purchase", -156.89),18]self.date ← 2026-01-15 00:00:00, self.description ← Grocery Store
pass 1 of 55class Transaction:6 def __init__(self⟨Transaction A⟩, date2026-01-15 00:00:00, descriptionGrocery Store, amount-45.67):7 self.date→ 2026-01-15 00:00:00 = date2026-01-15 00:00:008 self.description→ Grocery Store = descriptionGrocery Store9 self.amount→ -45.67 = amount-45.67All 5 passes — pass 1 is the card above pass selfdatedescriptionamountself.dateself.descriptionself.amount1 ⟨Transaction A⟩ 2026-01-15 00:00:00 Grocery Store -45.67 2026-01-15 00:00:00 Grocery Store -45.67 2 ⟨Transaction B⟩ 2026-01-18 00:00:00 Salary Deposit 2500.0 2026-01-18 00:00:00 Salary Deposit 2500.0 3 ⟨Transaction C⟩ 2026-01-20 00:00:00 Electric Bill -87.5 2026-01-20 00:00:00 Electric Bill -87.5 4 ⟨Transaction D⟩ 2026-01-22 00:00:00 Restaurant -32.4 2026-01-22 00:00:00 Restaurant -32.4 5 ⟨Transaction E⟩ 2026-01-25 00:00:00 Online Purchase -156.89 2026-01-25 00:00:00 Online Purchase -156.89 transactions ← [⟨Transaction A⟩, ⟨Transaction B⟩, ⟨Transaction C⟩, ⟨Transaction D⟩, ⟨Transaction E⟩]
11# Sample data12transactions→ [⟨Transaction A⟩, ⟨Transaction B⟩, ⟨Transaction C⟩, ⟨Transaction D⟩, ⟨Transaction E⟩] = [13 Transaction(datetime(2026, 1, 15), "Grocery Store", -45.67),14 Transaction(datetime(2026, 1, 18), "Salary Deposit", 2500.00),15 Transaction(datetime(2026, 1, 20), "Electric Bill", -87.50),16 Transaction(datetime(2026, 1, 22), "Restaurant", -32.40),17 Transaction(datetime(2026, 1, 25), "Online Purchase", -156.89),18]1920# Generate report21print("=" * 70)22print(f"{'BANK STATEMENT':^70}")23print("=" * 70)24print()2526print(f"{'Date':<12} {'Description':<25} {'Amount':>15}")27print("-" * 70)2829balance→ 1000.0 = 1000.0030for txn in transactions:output====================================================================== BANK STATEMENT ====================================================================== Date Description Amount ----------------------------------------------------------------------balance ← 954.33, date_str ← 2026-01-15, sign ← -, amount_str ← -$45.67
pass 1 of 529balance = 1000.0030for txn⟨Transaction A⟩ in transactions[⟨Transaction A⟩, ⟨Transaction B⟩, ⟨Transaction C⟩, ⟨Transaction D⟩, ⟨Transaction E⟩]:31 balance→ 954.33 += txn.amount-45.6732 date_str→ 2026-01-15 = txn.date2026-01-15 00:00:00.strftime("%Y-%m-%d")33 34 # Color-code positive/negative (using symbols)35 sign→ - = "+" if txn.amount-45.67 >= 0 else "-"36 amount_str→ -$45.67 = f"{sign-}${abs(txn.amount-45.67):.2f}"37 38 print(f"{date_str2026-01-15:<12} {txn.descriptionGrocery Store:<25} {amount_str-$45.67:>15}")output2026-01-15 Grocery Store -$45.67All 5 passes — pass 1 is the card above pass txntxn.amounttxn.datetxn.descriptionbalancedate_strsignamount_str1 ⟨Transaction A⟩ -45.67 2026-01-15 00:00:00 Grocery Store 1000.0 → 954.33 2026-01-15 - -$45.67 2 ⟨Transaction B⟩ 2500.0 2026-01-18 00:00:00 Salary Deposit 954.33 → 3454.33 2026-01-18 + +$2500.00 3 ⟨Transaction C⟩ -87.5 2026-01-20 00:00:00 Electric Bill 3454.33 → 3366.83 2026-01-20 - -$87.50 4 ⟨Transaction D⟩ -32.4 2026-01-22 00:00:00 Restaurant 3366.83 → 3334.43 2026-01-22 - -$32.40 5 ⟨Transaction E⟩ -156.89 2026-01-25 00:00:00 Online Purchase 3334.43 → 3177.54 2026-01-25 - -$156.89 total_deposits ← 2500.0, total_withdrawals ← -322.46
40print("-" * 70)41print(f"{'FINAL BALANCE:':<37} ${balance3177.54:>15,.2f}")42print("=" * 70)4344# Summary statistics45total_deposits→ 2500.0 = sum(t.amount(empty) for t in transactions[⟨Transaction A⟩, ⟨Transaction B⟩, ⟨Transaction C⟩, ⟨Transaction D⟩, ⟨Transaction E⟩] if t.amount > 0)46total_withdrawals→ -322.46 = sum(t.amount(empty) for t in transactions[⟨Transaction A⟩, ⟨Transaction B⟩, ⟨Transaction C⟩, ⟨Transaction D⟩, ⟨Transaction E⟩] if t.amount < 0)4748print()49print("SUMMARY")50print(f" Total Deposits: ${total_deposits2500.0:>10,.2f}")51print(f" Total Withdrawals: ${abs(total_withdrawals-322.46):>10,.2f}")52print(f" Net Change: ${total_deposits2500.0 + total_withdrawals-322.46:>10,.2f}")output---------------------------------------------------------------------- FINAL BALANCE: $ 3,177.54 ====================================================================== SUMMARY Total Deposits: $ 2,500.00 Total Withdrawals: $ 322.46 Net Change: $ 2,177.54
Exercise: practical.py
Create a formatted receipt with aligned columns, currency formatting, and totals