Substitution with string.Template is Python’s safe, lightweight, and readable way to replace placeholders in a template string with values from a mapping (dict). Using $identifier or ${identifier} syntax, it performs simple key-based substitution without evaluating arbitrary expressions — making it ideal for user-provided templates, configuration files, internationalization, or any scenario where security (no code injection) and simplicity matter more than advanced formatting. In 2026, string.Template remains useful — especially when format strings come from untrusted sources, legacy systems, or when you want strict separation of template and data without the power (and risk) of f-strings or .format(). It’s slower than f-strings but safer and more predictable.
Here’s a complete, practical guide to substitution with string.Template: basic usage, safe vs regular substitution, advanced placeholders, real-world patterns, comparison with other methods, and modern best practices with type hints, safety, and pandas/Polars integration.
Create a Template object with $var placeholders — then use .substitute() (raises on missing keys) or .safe_substitute() (leaves missing keys as-is).
from string import Template
# Basic substitution
tmpl = Template("Hello, $name! Welcome to $place.")
message = tmpl.substitute(name="Alice", place="Python World")
print(message)
# Hello, Alice! Welcome to Python World.
# Safe substitution — missing keys stay as $var
partial = Template("Hello, $name! Your score is $score.")
print(partial.safe_substitute(name="Bob"))
# Hello, Bob! Your score is $score.
Advanced placeholders — ${var} for disambiguation (when followed by letters/numbers), $$ for literal dollar sign.
price_tmpl = Template("Item: ${item}, Price: $$${price}")
print(price_tmpl.substitute(item="Coffee", price=4.50))
# Item: Coffee, Price: $4.50
complex_tmpl = Template("User ${user_id} ($${username}) logged in.")
print(complex_tmpl.substitute(user_id=123, username="alice"))
# User 123 ($alice) logged in.
Real-world pattern: secure templating with external/user-provided strings — Template prevents code execution (unlike f-strings) and injection risks.
# Safe email template from user config or database
email_tmpl = Template(user_config.get("greeting_template", "Hello $username!"))
email_body = email_tmpl.safe_substitute(
username="alice",
login_date="2026-02-10",
support_link="https://example.com/support"
)
print(email_body)
# Hello alice! (or whatever template was provided, missing vars unchanged)
Best practices make Template substitution safe, readable, and effective. Prefer .safe_substitute() with untrusted/partial data — avoids KeyError crashes and leaves missing placeholders intact. Use Template for user/config-provided templates — no expression evaluation, safer than f-strings or .format() with untrusted input. Modern tip: use Polars for large templated columns — pl.col("template").map_elements(lambda t: Template(t).safe_substitute(data)) works but prefer f-strings/.str.format() for speed. Add type hints — str — improves readability and mypy checks. Combine with dict.get() — Template(t).safe_substitute(data.get) for defaults. For complex formatting (alignment, precision), switch to f-strings or .format() — Template lacks specifiers. Avoid Template for performance-critical loops — f-strings are faster. Use Template with string.Template — no external dependencies. Handle missing keys gracefully — safe_substitute() preserves $var for debugging or partial fills.
Substitution with string.Template offers safe, predictable replacement — perfect for templates from config, users, or external sources. In 2026, use .safe_substitute() for robustness, prefer f-strings for speed/readability, vectorize in pandas/Polars when scaling, and add type hints for safety. Master Template substitution, and you’ll handle dynamic, secure text replacement cleanly and reliably.
Next time you have a template from config or user input — reach for string.Template. It’s Python’s cleanest way to say: “Substitute these values safely, no code execution.”