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:

:.2f
Format as float with 2 decimal places
3.14
:d
Format as integer
42
:e
Scientific notation
1.23e+02
:%
Percentage
75.50%
:,
Thousands separator
1,234,567
:>10
Right align in 10 characters
hello
:<10
Left align in 10 characters
hello
:^10
Center align in 10 characters
hello
:0>5
Pad with zeros, right align
00042
:x
Hexadecimal (lowercase)
ff
:X
Hexadecimal (uppercase)
FF
:b
Binary
1010

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})
%s
String
%d
Integer
%f
Float
%.2f
Float with 2 decimal places
%x
Hexadecimal
%o
Octal

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