Python String Formatting

· Seokhyeon Byun

Note: This post is based on my old programming study notes when I taught myself.

String Formatting

After understanding data types in Python, it is important to know how to format them properly.

Method 1: Old formatting using %

  • %d: integer
  • %s: string
  • %f: float
  • %c: character
number = 3
day = "several"
y = 'I ate %d apples. So I was happy for %s days.' % (number, day)
print(y)
# Output: I ate 3 apples. So I was happy for several days.

Method 2: Using {} with .format()

Case 1: Direct way

x = "I like {} very much".format('coding')
print(x)
# Output: I like coding very much

Case 2: Using variables inside string

x = 'I like {name} very much'.format(name="Python")
print(x)
# Output: I like Python very much

Advanced .format() usage:

# Multiple variables
text = "My name is {name} and I am {age} years old".format(name="Alice", age=25)

# Positional arguments
text = "The {0} is {1} years old".format("cat", 5)

# Number formatting
price = 49.95
text = "The price is {:.2f}".format(price)  # Output: The price is 49.95

Method 3: Using f-strings (Python 3.6+)

name = 'Elon Musk'
age = 52

z = f"This is {name}."
print(z)
# Output: This is Elon Musk.

# More examples
greeting = f"Hello, {name}! You are {age} years old."
calculation = f"2 + 3 = {2 + 3}"
formatted_number = f"Pi is approximately {3.14159:.2f}"

Escape Characters

Escape characters allow you to include special characters in strings that would otherwise be difficult to represent.

Common Escape Characters

  • Double quote: \"
  • Single quote: \'
  • Backslash: \\
  • Newline: \n
  • Tab: \t
  • Carriage return: \r

Examples

# Including quotes in strings
text1 = "He said, \"Hello World!\""
text2 = 'It\'s a beautiful day'

# Backslash in path
path = "C:\\Users\\Documents\\file.txt"

# Multi-line strings with newline
message = "First line\nSecond line\nThird line"

# Tab spacing
table = "Name\tAge\tCity"

Raw Strings

Use raw strings (prefix with r) to avoid interpreting escape characters:

# Regular string with escape characters
path1 = "C:\\Users\\Documents"

# Raw string - backslashes are treated literally
path2 = r"C:\Users\Documents"

# Both produce the same result but raw strings are cleaner for paths

Quick Reference

MethodSyntaxExampleBest Use Case
% formatting"text %s" % value"Hello %s" % nameLegacy code
.format()"text {}".format(value)"Hello {}".format(name)Python 2.7+ compatibility
f-stringsf"text {value}"f"Hello {name}"Modern Python (3.6+) - Recommended

Recommendation: Use f-strings for new code as they are more readable, faster, and less error-prone.


Technical Interview Essentials

Advanced f-string Techniques

1. Expression Evaluation

# Mathematical expressions
radius = 5
area = f"Circle area: {3.14159 * radius**2:.2f}"  # "Circle area: 78.54"

# Function calls
def get_user_name():
    return "Alice"

greeting = f"Hello, {get_user_name()}"  # "Hello, Alice"

# Method chaining
text = "python programming"
formatted = f"Title: {text.title().replace(' ', '-')}"  # "Title: Python-Programming"

2. Conditional Formatting

score = 85
grade = f"Grade: {'Pass' if score >= 60 else 'Fail'}"  # "Grade: Pass"

# Complex conditions
status = "active"
count = 42
message = f"User is {status} with {count} {'item' if count == 1 else 'items'}"

3. Format Specifiers for Numbers

number = 12345.6789

# Decimal places
formatted = f"{number:.2f}"      # "12345.68"

# Padding and alignment
formatted = f"{number:>10.2f}"   # "  12345.68" (right-aligned)
formatted = f"{number:<10.2f}"   # "12345.68  " (left-aligned)
formatted = f"{number:^10.2f}"   # " 12345.68 " (center-aligned)

# Zero padding
formatted = f"{number:010.2f}"   # "0012345.68"

# Thousands separator
formatted = f"{number:,.2f}"     # "12,345.68"

# Percentage
ratio = 0.85
formatted = f"{ratio:.1%}"       # "85.0%"

# Scientific notation
large_num = 1234567890
formatted = f"{large_num:.2e}"   # "1.23e+09"

4. Date and Time Formatting

from datetime import datetime

now = datetime.now()

# Various date formats
date_str = f"Today is {now:%Y-%m-%d}"           # "Today is 2025-07-15"
time_str = f"Current time: {now:%H:%M:%S}"      # "Current time: 14:30:45"
full_str = f"Full: {now:%A, %B %d, %Y}"        # "Full: Tuesday, July 15, 2025"

Performance Comparison (Interview Knowledge)

import timeit

name = "Python"
age = 10

# Performance test - f-strings are fastest!
def test_percent():
    return "Hello %s, you are %d years old" % (name, age)

def test_format():
    return "Hello {}, you are {} years old".format(name, age)

def test_fstring():
    return f"Hello {name}, you are {age} years old"

# f-strings are typically 2-3x faster than .format() and % formatting

Common Interview Patterns

1. Build Dynamic Strings

def build_sql_query(table, conditions):
    """Build SQL WHERE clause dynamically - common interview question"""
    where_parts = []
    for key, value in conditions.items():
        if isinstance(value, str):
            where_parts.append(f"{key} = '{value}'")
        else:
            where_parts.append(f"{key} = {value}")
    
    where_clause = " AND ".join(where_parts)
    return f"SELECT * FROM {table} WHERE {where_clause}"

# Usage
conditions = {"name": "Alice", "age": 25, "status": "active"}
query = build_sql_query("users", conditions)
print(query)
# SELECT * FROM users WHERE name = 'Alice' AND age = 25 AND status = 'active'

2. Format Data for Display

def format_user_table(users):
    """Format user data in table - common formatting task"""
    if not users:
        return "No users found."
    
    # Calculate column widths
    name_width = max(len(user['name']) for user in users)
    email_width = max(len(user['email']) for user in users)
    
    # Header
    header = f"{'Name':<{name_width}} | {'Email':<{email_width}} | {'Age':>3}"
    separator = "-" * len(header)
    
    # Build table
    lines = [header, separator]
    for user in users:
        line = f"{user['name']:<{name_width}} | {user['email']:<{email_width}} | {user['age']:>3}"
        lines.append(line)
    
    return "\n".join(lines)

# Test data
users = [
    {"name": "Alice Smith", "email": "alice@example.com", "age": 25},
    {"name": "Bob", "email": "bob@test.com", "age": 30},
    {"name": "Charlie Brown", "email": "charlie@demo.org", "age": 35}
]

print(format_user_table(users))

3. Parse and Format Log Entries

from datetime import datetime

def format_log_entry(level, message, timestamp=None):
    """Format log entry - practical programming task"""
    if timestamp is None:
        timestamp = datetime.now()
    
    # Color codes for different levels (useful for terminal output)
    colors = {
        "ERROR": "\033[91m",    # Red
        "WARNING": "\033[93m",  # Yellow  
        "INFO": "\033[92m",     # Green
        "DEBUG": "\033[94m",    # Blue
    }
    reset_color = "\033[0m"
    
    color = colors.get(level.upper(), "")
    formatted_time = timestamp.strftime("%Y-%m-%d %H:%M:%S")
    
    return f"[{formatted_time}] {color}{level.upper():<7}{reset_color} | {message}"

# Usage
print(format_log_entry("error", "Database connection failed"))
print(format_log_entry("info", "User logged in successfully"))
print(format_log_entry("warning", "Low disk space detected"))

String Validation & Sanitization

1. Input Validation

def validate_and_format_phone(phone):
    """Clean and validate phone number - common validation task"""
    # Remove all non-digit characters
    digits = ''.join(c for c in phone if c.isdigit())
    
    if len(digits) == 10:
        # Format as (XXX) XXX-XXXX
        return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}"
    elif len(digits) == 11 and digits[0] == '1':
        # Handle +1 country code
        return f"+1 ({digits[1:4]}) {digits[4:7]}-{digits[7:]}"
    else:
        return f"Invalid phone number: {phone}"

# Test cases
test_phones = ["1234567890", "123-456-7890", "(123) 456-7890", "1-123-456-7890"]
for phone in test_phones:
    print(f"{phone:<15} -> {validate_and_format_phone(phone)}")

2. Safe String Building (SQL Injection Prevention)

def safe_user_query(user_id, table="users"):
    """Demonstrate parameterized queries - security interview topic"""
    # WRONG - SQL injection vulnerable
    # Don't do: f"SELECT * FROM {table} WHERE id = {user_id}"
    
    # RIGHT - Use parameterized queries in real code
    # This is just for demonstration of string safety
    if not isinstance(user_id, int) or user_id <= 0:
        raise ValueError("User ID must be positive integer")
    
    if not table.replace('_', '').isalnum():
        raise ValueError("Invalid table name")
    
    return f"SELECT * FROM {table} WHERE id = ?"  # Placeholder for real parameterization

# Safe formatting for display/logging (not database queries)
def format_search_results(query_term, results_count):
    """Safe formatting for user input display"""
    # Escape special characters for display
    safe_term = query_term.replace('<', '&lt;').replace('>', '&gt;')
    return f"Found {results_count} results for '{safe_term}'"

Common Interview Gotchas

1. Late Binding with f-strings

# Interview trap - variable evaluation timing
functions = []

# WRONG - all functions will use the final value of i
for i in range(3):
    functions.append(lambda: f"Value is {i}")

for func in functions:
    print(func())  # All print "Value is 2"!

# CORRECT - capture the value at definition time
functions = []
for i in range(3):
    functions.append(lambda x=i: f"Value is {x}")

for func in functions:
    print(func())  # Prints "Value is 0", "Value is 1", "Value is 2"

2. Float Precision Issues

# Interview knowledge - floating point precision
value = 0.1 + 0.2
print(f"0.1 + 0.2 = {value}")           # "0.1 + 0.2 = 0.30000000000000004"
print(f"0.1 + 0.2 = {value:.1f}")       # "0.1 + 0.2 = 0.3" (rounded)

# For financial calculations, use Decimal
from decimal import Decimal
precise_value = Decimal('0.1') + Decimal('0.2')
print(f"Precise: {precise_value}")       # "Precise: 0.3"

3. Memory Efficiency

# For large-scale string operations, be memory conscious
def efficient_string_join(items):
    """Memory-efficient string concatenation"""
    # WRONG - O(n²) time complexity due to string immutability
    # result = ""
    # for item in items:
    #     result += f"{item}, "
    
    # RIGHT - O(n) time complexity
    formatted_items = [f"{item}" for item in items]
    return ", ".join(formatted_items)

# For very large datasets, consider generators
def format_large_dataset(data_generator):
    """Format large dataset without loading all into memory"""
    for item in data_generator:
        yield f"Processed: {item['name']} at {item['timestamp']}"