string.Template 是什么?
string.Template 是 Python string 模块的一部分,它实现了一种简单的字符串插值机制,你可以在一个字符串中定义“占位符”,然后用一个字典将占位符替换为实际的值。

核心思想: 将模板字符串和数据分离,使模板更易于管理和非程序员(如前端开发者)阅读。
基本语法: 占位符通常以 开头,后面跟着一个有效的 Python 标识符(字母、数字、下划线)。
$identifier: 最简单的形式,如$name。${identifier}: 当占位符后面紧跟着其他字母或数字时,需要用花括号 包裹,以明确结束位置。${age}years会正确替换为25years,而$ageyears会尝试查找一个不存在的ageyears变量。- 一个字面的 符号,为了在模板中表示一个真实的 ,你需要连续写两个 ,即 。
$$price会被渲染成$price。
如何使用 string.Template
使用 string.Template 主要分为三步:
- 创建模板对象:传入包含占位符的字符串。
- 准备数据:创建一个字典,键是模板中的占位符名,值是要替换的内容。
- 进行替换:调用模板对象的
substitute()或safe_substitute()方法,并传入数据字典。
示例代码
import string
# 1. 创建模板对象
# 模板字符串中包含了 $name, $action, $item 等占位符
template_str = """
亲爱的 $name:
您好!我们注意到您最近 $action 了您的购物车中的 $item。
这是一个非常棒的 $item!我们相信您会喜欢它。
祝好,
$store_name 团队
"""
template = string.Template(template_str)
# 2. 准备数据
# 字典的键必须与模板中的占位符名完全匹配
data = {
'name': '张三',
'action': '添加',
'item': 'Python编程书籍',
'store_name': '代码书店'
}
# 3. 进行替换
# 使用 substitute() 方法进行替换
final_message = template.substitute(data)
# 打印最终结果
print(final_message)
输出:

亲爱的 张三:
您好!我们注意到您最近 添加 了您的购物车中的 Python编程书籍。
这是一个非常棒的 Python编程书籍!我们相信您会喜欢它。
祝好,
代码书店 团队
substitute() vs safe_substitute()
这是 string.Template 中两个最核心的方法,理解它们的区别非常重要。
substitute(mapping, **kwargs)
- 功能:用提供的字典或关键字参数中的值替换模板中的所有占位符。
- 行为:如果模板中存在一个占位符,但在数据字典中没有找到对应的键,它会抛出
KeyError异常。 - 适用场景:当你确定模板中所有的占位符都会有对应的值时使用,这是一种“严格”模式。
safe_substitute(mapping, **kwargs)
- 功能:与
substitute()类似,替换所有能找到的占位符。 - 行为:如果模板中存在一个占位符,但在数据字典中没有找到对应的键,它会保留原始占位符不变,而不会抛出异常。
- 适用场景:当你不确定数据是否完整,或者希望即使某些数据缺失,模板仍然可以基本使用时,这是一种“宽松”或“安全”模式。
示例对比
import string
template_str = "你好, $name! 你的余额是 $balance。"
template = string.Template(template_str)
# 数据中缺少 'balance'
data_incomplete = {'name': '李四'}
# 使用 substitute() 会报错
try:
result = template.substitute(data_incomplete)
except KeyError as e:
print(f"使用 substitute() 时发生错误: {e}")
# 使用 safe_substitute() 不会报错,未匹配的占位符会保留
result_safe = template.safe_substitute(data_incomplete)
print("\n使用 safe_substitute() 的结果:")
print(result_safe)
输出:
使用 substitute() 时发生错误: 'balance'
使用 safe_substitute() 的结果:
你好, 李四! 你的余额是 $balance。
高级用法:自定义分隔符
默认情况下,string.Template 使用 作为分隔符,但在某些情况下,比如模板字符串本身可能包含很多 符号(如格式化货币 $100),或者你希望使用不同的语法(如 {{name}}),你可以自定义分隔符。
这需要创建一个继承自 string.Template 的子类,并重写 delimiter 属性。

示例:使用 和 作为分隔符
import string
# 1. 创建自定义模板类
class MyTemplate(string.Template):
# 将分隔符从 $ 改为 {{
delimiter = '{{'
# 将结束标识从 }} 改为 }}
# 默认的结束标识是 },我们需要调整逻辑来匹配 {{...}}
# 更简单的方式是使用 pattern
pattern = r'''
\{\{(?:
(?P<escaped>\{\{) | # 匹配转义的 {{
(?P<named>[_a-z][_a-z0-9]*)\}\} | # 匹配 {{identifier}}
(?P<braced>[_a-z][_a-z0-9]*)\}\} | # 匹配 {{identifier}} (为了清晰)
(?P<invalid>) # 其他情况,视为无效
)}}
'''
# 2. 使用自定义模板
template_str = "欢迎, {{user_name}}! 您的订单号是 {{order_id}}。"
template = MyTemplate(template_str)
# 3. 准备数据
data = {
'user_name': '王五',
'order_id': '12345'
}
# 4. 进行替换
final_message = template.substitute(data)
print(final_message)
输出:
欢迎, 王五! 您的订单号是 12345。
适用场景与优势
适用场景:
- 用户生成内容:当需要将用户输入(如评论、邮件内容)嵌入到模板中时,
Template的安全性至关重要。 - 配置文件模板:创建配置文件模板,让用户只需修改一个 values 文件(通常是 YAML 或 JSON)来改变应用行为。
- 多语言/本地化:将翻译文本与动态数据分离。
- 简单的报告生成:生成结构化的文本报告,如通知、日志摘要等。
优势:
- 安全性:这是
Template相比 格式化或str.format()最大的优势。Template的替换机制被设计为可以防止恶意输入,一个用户不能通过构造$__import__('os').system('rm -rf /')这样的字符串来执行任意代码。Template只会查找 后面的标识符,而不会执行 Python 表达式。 - 简单易读:语法非常直观,即使是不懂 Python 的人也能看懂模板。
- 关注点分离:将数据(字典)和展示(模板字符串)清晰地分开,符合软件工程的最佳实践。
与其他字符串格式化方法的对比
| 方法 | 示例 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 运算符 | "Hello, %s" % "World" |
老派、简单 | 语法笨拙,易错,不支持命名参数 | 快速简单的格式化,现代代码中不推荐 |
str.format() |
"Hello, {}".format("World") |
功能强大,灵活,支持索引、命名、格式化 | 对于简单场景语法略显冗长 | 通用字符串格式化,是现代 Python 的首选 |
| f-Strings (Python 3.6+) | name = "World"; f"Hello, {name}" |
最快,最易读,语法最简洁 | 仅限 Python 3.6+,变量必须在作用域内 | 绝大多数现代 Python 代码的首选 |
string.Template |
Template("Hello, $name").substitute({'name': "World"}) |
安全性高,语法简单,可自定义分隔符 | 功能相对较弱,性能不如 f-strings 和 format() |
处理用户输入、需要高安全性的模板场景 |
- 日常开发:优先使用 f-Strings,它们是性能和可读性的最佳结合。
- 需要高安全性:当处理来自不可信来源的数据(如用户输入)时,使用
string.Template。 - 复杂格式化:f-String 无法满足(需要非常复杂的条件格式化),可以使用
str.format()。 - 维护旧代码:可能会遇到 运算符。
