The string module provides useful constants and a Template class for simple string substitution. Less common than f-strings, but useful in specific situations.

Character Constants

Pre-defined character sets:

import string
 
print(string.ascii_lowercase)  # abcdefghijklmnopqrstuvwxyz
print(string.ascii_uppercase)  # ABCDEFGHIJKLMNOPQRSTUVWXYZ
print(string.ascii_letters)    # abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
print(string.digits)           # 0123456789
print(string.hexdigits)        # 0123456789abcdefABCDEF
print(string.octdigits)        # 01234567
print(string.punctuation)      # !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
print(string.whitespace)       # ' \t\n\r\x0b\x0c'
print(string.printable)        # All printable characters

Password Generation

import string
import secrets
 
def generate_password(length=16):
    """Generate a secure random password."""
    alphabet = string.ascii_letters + string.digits + string.punctuation
    return ''.join(secrets.choice(alphabet) for _ in range(length))
 
print(generate_password())  # e.g., "K#9mP$xL2!qR@nTz"

Input Validation

import string
 
def is_valid_username(username):
    """Check if username contains only allowed characters."""
    allowed = string.ascii_letters + string.digits + '_'
    return all(char in allowed for char in username)
 
print(is_valid_username('alice_123'))  # True
print(is_valid_username('alice@123'))  # False

String Cleaning

import string
 
def remove_punctuation(text):
    """Remove all punctuation from text."""
    translator = str.maketrans('', '', string.punctuation)
    return text.translate(translator)
 
text = "Hello, world! How's it going?"
print(remove_punctuation(text))  # "Hello world Hows it going"

Template Strings

Safe string substitution:

from string import Template
 
# Basic substitution
t = Template('Hello, $name!')
print(t.substitute(name='Alice'))  # Hello, Alice!
 
# With dictionary
data = {'name': 'Bob', 'city': 'NYC'}
t = Template('$name lives in $city')
print(t.substitute(data))  # Bob lives in NYC

Safe Substitute

Handle missing keys gracefully:

from string import Template
 
t = Template('Hello, $name! Your balance is $balance')
 
# substitute() raises KeyError for missing keys
try:
    t.substitute(name='Alice')
except KeyError as e:
    print(f"Missing: {e}")
 
# safe_substitute() leaves missing keys unchanged
result = t.safe_substitute(name='Alice')
print(result)  # Hello, Alice! Your balance is $balance

Template Syntax

from string import Template
 
# $identifier - simple substitution
t = Template('$greeting, $name')
 
# ${identifier} - for adjacent text
t = Template('${noun}ification')
print(t.substitute(noun='gamif'))  # gamification
 
# $$ - literal dollar sign
t = Template('Price: $$${price}')
print(t.substitute(price='99'))  # Price: $99

Why Template Over f-strings?

Templates are useful when:

  1. User-provided templates - safer than eval/exec
  2. Configuration files - simple substitution syntax
  3. Internationalization - templates can be stored externally
from string import Template
 
# User-provided template (safe)
user_template = Template('Dear $name, your order #$order_id is ready.')
result = user_template.substitute(name='Alice', order_id='12345')
 
# f-strings with user input would be dangerous
# Never do: eval(f"f'{user_input}'")

Custom Delimiters

Change the substitution character:

from string import Template
 
class PercentTemplate(Template):
    delimiter = '%'
 
t = PercentTemplate('Hello, %name!')
print(t.substitute(name='Alice'))  # Hello, Alice!

Formatter Class

Lower-level formatting control:

import string
 
formatter = string.Formatter()
 
# Parse format string
parsed = list(formatter.parse('{name} is {age} years old'))
for literal, field, format_spec, conversion in parsed:
    print(f"Field: {field}, Literal: {literal!r}")

Practical Example: Email Templates

from string import Template
 
email_template = Template("""
Dear $name,
 
Thank you for your purchase of $product.
Your order number is $order_id.
 
Total: $$${total}
 
Best regards,
The Team
""")
 
def send_confirmation(name, product, order_id, total):
    body = email_template.substitute(
        name=name,
        product=product,
        order_id=order_id,
        total=total
    )
    print(body)
 
send_confirmation('Alice', 'Widget Pro', '12345', '99.99')

Config File Pattern

from string import Template
import os
 
config_template = Template("""
database:
  host: $DB_HOST
  port: $DB_PORT
  name: $DB_NAME
""")
 
# Substitute from environment
config = config_template.safe_substitute(os.environ)

Capwords

Titlecase with proper spacing:

import string
 
text = "hello   world   example"
 
# string.capwords handles multiple spaces
print(string.capwords(text))  # "Hello World Example"
 
# Compare to str.title()
print(text.title())  # "Hello   World   Example" (preserves spaces)

When to Use string Module

Use string constants when:

  • Generating random strings
  • Validating input characters
  • Cleaning/filtering text
  • Need a reference set of characters

Use Template when:

  • User-provided format strings (security)
  • Simple key-value substitution
  • External template files
  • i18n/l10n scenarios

Use f-strings/format() when:

  • Complex formatting needed
  • Expressions in templates
  • Performance matters
  • Developer-controlled templates

The string module fills specific niches—character constants and safe templating—that come up more often than you might expect.

React to this post: