Python String Formatting
Learn different methods to format strings in Python including f-strings, format(), and % formatting.
String Formatting
To make sure a string will display as expected, we can format the result with the format() method.
String format()
The format() method allows you to format selected parts of a string.
Sometimes there are parts of a text that you do not control, maybe they come from a database, or user input?
To control such values, add placeholders (curly brackets {}) in the text, and run the values through the format() method:
Example - Add a placeholder where you want to display the price:
price = 49
txt = "The price is {} dollars"
print(txt.format(price))You can add parameters inside the curly brackets to specify how to convert the value:
Example - Format the price to be displayed as a number with two decimals:
price = 49
txt = "The price is {:.2f} dollars"
print(txt.format(price))Multiple Values
If you want to use more values, just add more values to the format() method:
Example
quantity = 3
itemno = 567
price = 49
myorder = "I want {} pieces of item number {} for {:.2f} dollars."
print(myorder.format(quantity, itemno, price))Index Numbers
You can use index numbers (a number inside the curly brackets {0}) to be sure the values are placed in the correct placeholders:
Example
quantity = 3
itemno = 567
price = 49
myorder = "I want {0} pieces of item number {1} for {2:.2f} dollars."
print(myorder.format(quantity, itemno, price))Also, if you want to refer to the same value more than once, use the index number:
Example
age = 36
name = "John"
txt = "His name is {1}. {1} is {0} years old."
print(txt.format(age, name))Named Indexes
You can also use named indexes by entering a name inside the curly brackets {carname}, but then you must use names when you pass the parameter values txt.format(carname = "Ford"):
Example
myorder = "I have a {carname}, it is a {model}."
print(myorder.format(carname = "Ford", model = "Mustang"))F-Strings (Formatted String Literals)
F-strings were introduced in Python 3.6 and provide a more readable and concise way to format strings:
Example - Basic f-string usage:
name = "John"
age = 36
txt = f"My name is {name} and I am {age} years old"
print(txt)Example - F-strings with expressions:
price = 59
tax_rate = 0.08
total = price * (1 + tax_rate)
txt = f"Price: ${price}, Tax: {tax_rate:.1%}, Total: ${total:.2f}"
print(txt)Example - F-strings with function calls:
import datetime
name = "Alice"
now = datetime.datetime.now()
txt = f"Hello {name.upper()}, today is {now.strftime('%Y-%m-%d')}"
print(txt)Format Specifiers
Format specifiers allow you to control how values are displayed:
Advanced Formatting Examples
Number Formatting
number = 1234567.89
# Different number formats
print(f"Default: {number}")
print(f"Two decimals: {number:.2f}")
print(f"Thousands separator: {number:,.2f}")
print(f"Scientific notation: {number:.2e}")
print(f"Percentage: {0.1234:.2%}")
print(f"Padded: {42:08d}")
print(f"Hexadecimal: {255:x}")
print(f"Binary: {10:b}")
# Currency formatting
price = 1234.56
print(f"Price: ${price:,.2f}")
print(f"Price (padded): ${price:>10,.2f}")String Alignment and Padding
text = "Python"
# Alignment examples
print(f"Left aligned: '{text:<15}'")
print(f"Right aligned: '{text:>15}'")
print(f"Center aligned: '{text:^15}'")
print(f"Padded with dots: '{text:.<15}'")
print(f"Padded with stars: '{text:*^15}'")
# Creating a simple table
items = [("Apple", 1.20), ("Banana", 0.50), ("Orange", 0.80)]
print("\nFruit Price List:")
print("-" * 20)
for item, price in items:
print(f"{item:<10} ${price:>6.2f}")Date and Time Formatting
import datetime
now = datetime.datetime.now()
birthday = datetime.date(1990, 5, 15)
# Date formatting with f-strings
print(f"Current time: {now}")
print(f"Formatted date: {now:%Y-%m-%d}")
print(f"Formatted time: {now:%H:%M:%S}")
print(f"Full format: {now:%A, %B %d, %Y at %I:%M %p}")
print(f"Birthday: {birthday:%B %d, %Y}")
# Age calculation
age = (datetime.date.today() - birthday).days // 365
print(f"Age: {age} years old")Old-Style % Formatting
Python also supports the older % formatting method (similar to C's printf):
Example - % formatting:
name = "John"
age = 36
height = 5.9
# Basic % formatting
print("My name is %s" % name)
print("I am %d years old" % age)
print("My height is %.1f feet" % height)
# Multiple values
print("My name is %s and I am %d years old" % (name, age))
# Named placeholders
print("%(name)s is %(age)d years old" % {"name": name, "age": age})Practical Examples
Report Generation
def generate_sales_report(sales_data):
"""Generate a formatted sales report"""
print("=" * 50)
print(f"{'SALES REPORT':^50}")
print("=" * 50)
total_sales = 0
total_quantity = 0
# Header
print(f"{'Product':<20} {'Qty':>8} {'Price':>10} {'Total':>10}")
print("-" * 50)
# Data rows
for product, quantity, price in sales_data:
total = quantity * price
total_sales += total
total_quantity += quantity
print(f"{product:<20} {quantity:>8d} ${price:>9.2f} ${total:>9.2f}")
# Summary
print("-" * 50)
print(f"{'TOTALS':<20} {total_quantity:>8d} {'':<10} ${total_sales:>9.2f}")
print("=" * 50)
# Statistics
avg_price = total_sales / total_quantity if total_quantity > 0 else 0
print(f"Average price per item: ${avg_price:.2f}")
print(f"Total items sold: {total_quantity:,}")
print(f"Total revenue: ${total_sales:,.2f}")
# Sample data
sales = [
("Laptop", 5, 999.99),
("Mouse", 25, 29.99),
("Keyboard", 15, 79.99),
("Monitor", 8, 299.99),
("Headphones", 12, 149.99)
]
generate_sales_report(sales)Configuration File Generator
def generate_config_file(config_data):
"""Generate a formatted configuration file"""
config_template = """
# Application Configuration File
# Generated on {timestamp}
[Database]
host = {db_host}
port = {db_port}
database = {db_name}
username = {db_user}
password = {db_pass}
[Server]
host = {server_host}
port = {server_port}
debug = {debug_mode}
workers = {worker_count}
[Logging]
level = {log_level}
file = {log_file}
max_size = {log_max_size}MB
backup_count = {log_backup_count}
[Features]
cache_enabled = {cache_enabled}
rate_limiting = {rate_limiting}
api_version = v{api_version}
"""
import datetime
# Add timestamp
config_data['timestamp'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# Format the configuration
formatted_config = config_template.format(**config_data)
return formatted_config.strip()
# Configuration data
config = {
'db_host': 'localhost',
'db_port': 5432,
'db_name': 'myapp',
'db_user': 'admin',
'db_pass': '********',
'server_host': '0.0.0.0',
'server_port': 8000,
'debug_mode': False,
'worker_count': 4,
'log_level': 'INFO',
'log_file': '/var/log/myapp.log',
'log_max_size': 100,
'log_backup_count': 5,
'cache_enabled': True,
'rate_limiting': True,
'api_version': '2.1'
}
print(generate_config_file(config))Progress Bar with Formatting
import time
import sys
def show_progress(current, total, bar_length=40, prefix="Progress"):
"""Display a formatted progress bar"""
# Calculate percentage
percent = (current / total) * 100
# Calculate filled length
filled_length = int(bar_length * current // total)
# Create the bar
bar = '█' * filled_length + '-' * (bar_length - filled_length)
# Format the progress line
progress_line = f"\r{prefix}: |{bar}| {percent:.1f}% ({current}/{total})"
# Print without newline
sys.stdout.write(progress_line)
sys.stdout.flush()
# Print newline when complete
if current == total:
print()
def simulate_task():
"""Simulate a task with progress updates"""
total_steps = 100
print("Starting task simulation...")
for i in range(total_steps + 1):
show_progress(i, total_steps, prefix="Processing")
time.sleep(0.05) # Simulate work
print("Task completed!")
# Uncomment to run the simulation
# simulate_task()
# Static example of different progress states
print("Progress bar examples:")
show_progress(0, 100, prefix="Starting")
show_progress(25, 100, prefix="Quarter")
show_progress(50, 100, prefix="Half")
show_progress(75, 100, prefix="Three-quarters")
show_progress(100, 100, prefix="Complete")Data Table Formatter
def format_table(data, headers, title=None):
"""Format data as a nice table"""
if not data or not headers:
return "No data to display"
# Calculate column widths
col_widths = []
for i, header in enumerate(headers):
max_width = len(str(header))
for row in data:
if i < len(row):
max_width = max(max_width, len(str(row[i])))
col_widths.append(max_width + 2) # Add padding
# Create format string
row_format = "|".join(f"{{:<{width}}}" for width in col_widths)
separator = "+" + "+".join("-" * width for width in col_widths) + "+"
# Build the table
table_lines = []
# Title
if title:
total_width = sum(col_widths) + len(col_widths) - 1
table_lines.append("=" * total_width)
table_lines.append(f"{title:^{total_width}}")
table_lines.append("=" * total_width)
# Headers
table_lines.append(separator)
table_lines.append("|" + row_format.format(*headers) + "|")
table_lines.append(separator)
# Data rows
for row in data:
# Pad row if necessary
padded_row = list(row) + [""] * (len(headers) - len(row))
table_lines.append("|" + row_format.format(*padded_row) + "|")
table_lines.append(separator)
return "\n".join(table_lines)
# Example usage
employee_data = [
["John Doe", "Developer", 75000, "2020-01-15"],
["Jane Smith", "Designer", 65000, "2019-03-22"],
["Bob Johnson", "Manager", 85000, "2018-07-10"],
["Alice Brown", "Analyst", 60000, "2021-05-03"]
]
headers = ["Name", "Position", "Salary", "Start Date"]
print(format_table(employee_data, headers, "Employee Information"))
# Financial data example
financial_data = [
["Q1 2023", 125000, 98000, 27000],
["Q2 2023", 142000, 105000, 37000],
["Q3 2023", 138000, 102000, 36000],
["Q4 2023", 156000, 115000, 41000]
]
financial_headers = ["Quarter", "Revenue", "Expenses", "Profit"]
print("\n" + format_table(financial_data, financial_headers, "Quarterly Financial Report"))Best Practices
- Use f-strings for modern Python (3.6+) - They're more readable and performant
- Use .format() for older Python versions or when you need more complex formatting
- Avoid % formatting in new code unless maintaining legacy systems
- Be consistent - Choose one formatting method and stick with it in your project
- Use meaningful variable names in format strings for clarity
- Consider locale-specific formatting for international applications
- Use format specifiers to control number precision and alignment
Three Ways to Format — Use f-strings
| Style | Example | Verdict |
|---|---|---|
| f-string | f"Hi {name}" | Best — modern, fast, readable |
| .format() | "Hi {}".format(name) | OK — older code |
| % operator | "Hi %s" % name | Legacy — avoid |
Format Specifiers
After a colon inside { } you control width, precision, alignment, and more.
pi = 3.14159
price = 2500
print(f"{pi:.2f}") # 3.14 -> 2 decimals
print(f"{price:,}") # 2,500 -> thousands separator
print(f"{price:>8}") # ' 2500' -> right align width 8
print(f"{0.25:.0%}") # 25% -> percentage
print(f"{255:#x}") # 0xff -> hexadecimal
print(f"{42:04d}") # 0042 -> zero-paddedTry It Yourself
Exercise 1: Format 1234.5678 with exactly two decimal places and a thousands separator.
Show solution
x = 1234.5678
print(f"{x:,.2f}") # 1,234.57Exercise 2: Print 0.875 as a whole-number percentage.
Show solution
print(f"{0.875:.0%}") # 88%Key Takeaways
- Prefer f-strings — they embed expressions directly.
- Control output with format specs:
:.2f,:,,:>10,:%. - Padding and alignment make clean tabular text output.
📘 Real-World Deep Dive
Three formatting APIs exist: C-style <code>%</code>, <code>str.format</code>, and f-strings. F-strings are the obvious winner for new code: faster, more readable, and great for inline expressions.
Real-Life Scenario
A small log-emitter that formats size, time, percent, and structured outputs — using width/precision/alignment specifiers without breaking readability.
Real-Life Example
import time, datetime as dt
from decimal import Decimal
def fmt_bytes(n: int) -> str:
for unit, div in [("GiB", 1024**3), ("MiB", 1024**2), ("KiB", 1024)]:
if n >= div:
return f"{n/div:8.2f} {unit}"
return f"{n:>9d} B"
now = dt.datetime.now()
print(f"{now:%Y-%m-%d %H:%M:%S} request_id={now.microsecond:06d}")
print(f"uploaded : {fmt_bytes(15_728_640)}")
print(f"rate : {0.8723:.2%}")
print(f"padding : |{ 'aligned':<12} | {'right':>12} | {'center':^12} |")
total = Decimal("19.99")
print(f"price : {total:.2f}")
print(f"hex : {now.microsecond:08x}")
Expected Output
2026-08-20 21:48:30 request_id=214830
uploaded : 15.00 MiB
rate : 87.23%
padding : |aligned | right | center |
price : 19.99
hex : 000347f6Common mistakes
f"{x:.2f}"rounds on display, butxitself stays the original — don't confuse formatting with precision.- Mixing
{user.name}with multi-line strings with{ in literal text }produces aSyntaxError— escape with{{/}}. - Width specifiers without alignment (
{x:5}) right-align by default for numbers; left-align for strings.
🚀 Performance & Best Practices
- F-strings compile to the fastest bytecode path;
"{}".format(...)and%are slower. - Avoid formatting inside tight loops — format the final output once.
- For thousand-separators use
{x:,}; for fixed decimals,{x:.2f}; for percentages,{x:.2%}.
🧪 Try It Yourself
- Build a tabular
print_row(*cells)helper that pads to the widest cell. - Wrap a logger so messages render with fixed-width level + timestamp.
- Refactor an existing
%-formatted message to f-string and benchmark.