name = "World" # f-string 语法 formatted = f"Hello {name}!" print(type(formatted)) # 输出:<class 'str'> print(formatted) # 输出:Hello World! # t-string 语法 templated = t"Hello {name}!" print(type(templated)) # 输出:<class 'string.templatelib.Template'> print(templated.strings) # 输出:('Hello ', '') print(templated.interpolations[0].value) # 输出:World print("".join( item if isinstance(item, str) else str(item.value) for item in templated )) # 输出:Hello World!如上所述,t-string 与 f-string 不同的是,它不会立即得到普通字符串,而是返回一个新的类型 Template(来自 Python 3.14 新增的标准库模块 string.templatelib)。这个类型不能直接作为普通字符串输出,但它提供了对字符串和其内部插值表达式的结构化访问,使得开发者能够在字符串组合插入值前添加拦截和转换。一句话总结 t-string 的核心特点就是延迟渲染。
# 使用 f-string 的不安全示例(SQL 注入风险) sql_query = f"SELECT * FROM users WHERE name = '{user_input}'" # 堆代码 duidaima.com # 使用 f-string 的不安全示例(XSS 风险) html_content = f"<div>{user_input}</div>"而 t-string 允许开发者在字符串组合前对插值进行适当处理:
# 使用 t-string 的安全示例 evil = "<script>alert('evil')</script>" template = t"<p>{evil}</p>" # 可以定义处理函数来转义内容 assert html(template) == "<p><script>alert('evil')</script></p>"增强字符串处理的灵活性
from string.templatelib import Template, Interpolation data = {"name": "Python猫", "age": 18} template = t"用户 {data['name']} 的年龄是 {data['age']}" def standard_renderer(template: Template) -> str: """标准文本渲染""" return"".join( item if isinstance(item, str) else str(item.value) for item in template ) def json_renderer(template: Template) -> str: """JSON格式渲染""" import json, re values = {} for item in template: ifnot isinstance(item, str): # 使用正则表达式从表达式中提取键名 # 匹配 data['name'] 或 data["name"] 模式中的name match = re.search(r"\['([^']+)'\]|\[\"([^\"]+)\"\]", item.expression) if match: # 获取匹配的第一个分组 key = match.group(1) if match.group(1) else match.group(2) values[key] = item.value return json.dumps(values, ensure_ascii=False) def xml_renderer(template: Template) -> str: """XML格式渲染""" parts = ["<data>"] for item in template: ifnot isinstance(item, str) and hasattr(item, "expression"): name = item.expression.split("'")[1] if"'"in item.expression else item.expression parts.append(f" <{name}>{item.value}</{name}>") parts.append("</data>") return"\n".join(parts) # 同一个模板,不同的输出格式 print(standard_renderer(template)) # 输出: 用户 Python猫 的年龄是 18 print(json_renderer(template)) # 输出: {"name": "Python猫", "age": 18} print(xml_renderer(template)) # 输出: <data>\n <name>Python猫</name>\n <age>18</age>\n</data>这种灵活性是 f-string 所不具备的,对于构建各种 DSL(领域特定语言)、模板引擎或格式化系统非常有价值。
class Template: strings: tuple[str, ...] """ 模板中字符串部分的非空元组。 包含 N+1 个元素,其中 N 是模板中插值表达式的数量。 """ interpolations: tuple[Interpolation, ...] """ 模板中插值部分的元组。 如果没有插值表达式,这将是一个空元组。 """ def __new__(cls, *args: str | Interpolation): """ 创建一个新的 Template 实例。 参数可以按任意顺序提供。 """ ... @property def values(self) -> tuple[object, ...]: """ 返回模板中每个 Interpolation 的 `value` 属性组成的元组。 如果没有插值表达式,这将是一个空元组。 """ ... def __iter__(self) -> Iterator[str | Interpolation]: """ 迭代模板中的字符串部分和插值表达式。 这些可能以任意顺序出现。不包含空字符串。 """ ...这种结构使开发者能够:
处理模型:f-string 是"即时完成"模型,t-string 是"预处理+转换"模型
def html(template: Template) -> str: parts = [] for item in template: if isinstance(item, str): parts.append(item) else: # Interpolation parts.append(html_escape(item.value)) return "".join(parts) user_input = "<script>alert('XSS')</script>" safe_html = html(t"<div>{user_input}</div>") # 输出: <div><script>alert('XSS')</script></div>2. 安全的 SQL 查询构建
def safe_sql(template: Template) -> str: parts = [] params = [] for item in template: if isinstance(item, str): parts.append(item) else: parts.append("?") params.append(item.value) return"".join(parts), params user_id = "user' OR 1=1--" query, params = safe_sql(t"SELECT * FROM users WHERE id = {user_id}") # query: "SELECT * FROM users WHERE id = ?" # params: ["user' OR 1=1--"]3. 结构化日志
import json import logging from string.templatelib import Template, Interpolation class TemplateMessage: def __init__(self, template: Template) -> None: self.template = template @property def message(self) -> str: # 格式化为可读消息 return f(self.template) # 使用自定义 f() 函数 @property def values(self) -> dict: # 提取结构化数据 return { item.expression: item.value for item in self.template.interpolations } def __str__(self) -> str: returnf"{self.message} >>> {json.dumps(self.values)}" action, amount, item = "traded", 42, "shrubs" logging.info(TemplateMessage(t"User {action}: {amount:.2f} {item}")) # 输出: User traded: 42.00 shrubs >>> {"action": "traded", "amount": 42, "item": "shrubs"}4. 安全的子进程调用
# 不安全的 f-string 用法 subprocess.run(f"echo {message_from_user}", shell=True) # 命令注入风险 # 安全的 t-string 用法 subprocess.run(t"echo {message_from_user}") # 自动进行适当的命令转义这种方式既保留了字符串命令的可读性,又避免了安全风险。
from string.templatelib import Template, Interpolation import html def smart_renderer(template: Template, context="text") -> str: """上下文感知的渲染器 根据context参数自动决定如何处理每个插值: - "text": 普通文本模式,直接转为字符串 - "html": HTML模式,自动转义HTML特殊字符,防止XSS - "sql": SQL模式,自动转义SQL特殊字符,防止注入 """ parts = [] for item in template: if isinstance(item, str): parts.append(item) else: # Interpolation value = item.value expression = item.expression # 基于值类型和上下文进行智能处理 if context == "html": # HTML模式:自动转义HTML特殊字符 parts.append(html.escape(str(value))) elif context == "sql": # SQL模式:防止SQL注入 if isinstance(value, str): # 将1个单引号转义成2个 escaped_value = value.replace("'", "''") parts.append(f"'{escaped_value}'") elif value isNone: parts.append("NULL") else: parts.append(str(value)) else: parts.append(str(value)) return"".join(parts) # 同一个模板在不同上下文中的自动适配渲染 user_input = "<script>alert('evil')</script>" template = t"用户输入: {user_input}" print(smart_renderer(template, context="html")) # 输出: 用户输入: <script>alert('evil')</script> # SQL注入防护示例 user_id = "1'; DROP TABLE users; --" sql_template = t"SELECT * FROM users WHERE id = {user_id}" print(smart_renderer(sql_template, context="sql")) # 输出: SELECT * FROM users WHERE id = '1''; DROP TABLE users; --' # f-string 对于SQL注入,必须先处理值,再放入f-string escaped_id = user_id.replace("'", "''") sql_safe_id = f"'{escaped_id}'" print(f"SQL查询(f-string): SELECT * FROM users WHERE id = {sql_safe_id}")2. 结构化嵌套模板处理
# f-string的嵌套:内部表达式立即求值,信息丢失 value = "world" inner_f = f"inner {value}" outer_f = f"outer {inner_f}" print(outer_f) # 输出: outer inner world print(type(outer_f)) # <class 'str'> - 只是普通字符串 # t-string的嵌套:保留完整结构信息 inner_t = t"inner {value}" outer_t = t"outer {inner_t}" print(type(outer_t)) # <class 'string.templatelib.Template'> print(type(outer_t.interpolations[0].value)) # 也是Template对象! # 可以访问和处理任意深度的嵌套结构 user = {"name": "Alice", "age": 30} message = t"用户{user['name']}信息: {t'年龄:{user['age']}'}" inner_template = message.interpolations[1].value print(inner_template.strings) # 输出: ('年龄:', '') print(inner_template.interpolations[0].value) # 输出: 30这种结构化处理能力使 t-string 特别适合构建复杂的模板系统,可以按需延迟或自定义渲染过程的所有部分。
import asyncio # 模拟异步数据获取 asyncdef fetch_data(key: str) -> str: await asyncio.sleep(0.1) # 模拟网络延迟 returnf"获取的{key}数据" asyncdef render_async(template): tasks = {} # 并行启动所有异步查询 for item in template.interpolations: tasks[item.expression] = asyncio.create_task( fetch_data(item.expression) ) # 等待所有查询完成 for expr, task in tasks.items(): tasks[expr] = await task # 组装结果 result = [] for item in template: if isinstance(item, str): result.append(item) else: result.append(tasks[item.expression]) return"".join(result) asyncdef main(): template = t"用户: {user}, 年龄: {age}" result = await render_async(template) print(result) # asyncio.run(main())这种模式的关键优势:
延迟组合: 等所有数据就绪再拼接