Python 3.14 t-string 要来了,它与 f-string 有何不同?

打印 上一主题 下一主题

主题 1710|帖子 1710|积分 5132

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
Python 最近出了个大新闻:PEP-750 t-string 语法被正式接纳了!
这意味着 Python 将在今年 10 月发布的 3.14 版本中引入一种新的字符串前缀 t,称为模板字符串(Template Strings),即 t-string。
这是继 f-string 之后,字符串处理本领的重大升级,旨在提供更安全、更灵活的字符串插值处理机制。
t-string 的基本语法与 f-string 非常相似,但得到的结果却大为不同:
  1. name = "World"
  2. # f-string 语法
  3. formatted = f"Hello {name}!"
  4. print(type(formatted))  # 输出:<class 'str'>
  5. print(formatted)  # 输出:Hello World!
  6. # t-string 语法
  7. templated = t"Hello {name}!"
  8. print(type(templated))  # 输出:<class 'string.templatelib.Template'>
  9. print(templated.strings)  # 输出:('Hello ', '')
  10. print(templated.interpolations[0].value)  # 输出:World
  11. print("".join(
  12.         item if isinstance(item, str) else str(item.value)
  13.         for item in templated
  14.     ))  # 输出:Hello World!
复制代码
如上所述,t-string 与 f-string 不同的是,它不会立即得到普通字符串,而是返回一个新的范例 Template(来自 Python 3.14 新增的标准库模块 string.templatelib)。
这个范例不能直接作为普通字符串输出,但它提供了对字符串和其内部插值表达式的布局化访问,使得开发者能够在字符串组合插入值前添加拦截和转换。
一句话总结 t-string 的焦点特点就是延迟渲染
为什么要计划 t-string?

f-string 因其简洁易用而广受欢迎,但它也存在一些无法忽视的局限性:

  • 安全隐患:当直接将用户输入嵌入到 SQL 查询、HTML 内容或系统命令中时,f-string 可能导致注入攻击
  • 缺乏转换本领:f-string 没有提供在字符串组合前拦截和转换插入值的机制
  • 灵活性不足:对于复杂的字符串处理任务,f-string 的本领有限
提升字符串处理的安全性

不谨慎利用 f-string,可能导致安全毛病:
  1. # 使用 f-string 的不安全示例(SQL 注入风险)
  2. sql_query = f"SELECT * FROM users WHERE name = '{user_input}'"
  3. # 使用 f-string 的不安全示例(XSS 风险)
  4. html_content = f"{user_input}"
复制代码
而 t-string 允许开发者在字符串组合前对插值举行恰当处理:
  1. # 使用 t-string 的安全示例
  2. evil = ""
  3. template = t"<p>{evil}</p>"
  4. # 可以定义处理函数来转义内容
  5. assert html(template) == "<p><script>alert('evil')</script></p>"
复制代码
增强字符串处理的灵活性

t-string 最大的上风在于它提供了同一的字符串处理机制,让开发者可以根据现实需求实现各种自定义渲染逻辑。这种计划克制了为每种场景创建专门语法的复杂性,同时保持了 Python 简洁同一的风格。
以下示例展示了怎样基于同一个 t-string 模板,利用不同的渲染器输出不同的格式:
  1. from string.templatelib import Template, Interpolation
  2. data = {"name": "Python猫", "age": 18}
  3. template = t"用户 {data['name']} 的年龄是 {data['age']}"
  4. def standard_renderer(template: Template) -> str:
  5.     """标准文本渲染"""
  6.     return "".join(
  7.         item if isinstance(item, str) else str(item.value)
  8.         for item in template
  9.     )
  10. def json_renderer(template: Template) -> str:
  11.     """JSON格式渲染"""
  12.     import json, re
  13.     values = {}
  14.     for item in template:
  15.         if not isinstance(item, str):
  16.             # 使用正则表达式从表达式中提取键名
  17.             # 匹配 data['name'] 或 data["name"] 模式中的name
  18.             match = re.search(r"\['([^']+)'\]|\["([^"]+)"\]", item.expression)
  19.             if match:
  20.                 # 获取匹配的第一个分组
  21.                 key = match.group(1) if match.group(1) else match.group(2)
  22.                 values[key] = item.value
  23.     return json.dumps(values, ensure_ascii=False)
  24.    
  25. def xml_renderer(template: Template) -> str:
  26.     """XML格式渲染"""
  27.     parts = ["<data>"]
  28.     for item in template:
  29.         if not isinstance(item, str) and hasattr(item, "expression"):
  30.             name = item.expression.split("'")[1] if "'" in item.expression else item.expression
  31.             parts.append(f"  <{name}>{item.value}</{name}>")
  32.     parts.append("</data>")
  33.     return "\n".join(parts)
  34. # 同一个模板,不同的输出格式
  35. print(standard_renderer(template))  # 输出: 用户 Python猫 的年龄是 18
  36. print(json_renderer(template))      # 输出: {"name": "Python猫", "age": 18}
  37. print(xml_renderer(template))       # 输出: <data>\n  <name>Python猫</name>\n  <age>18</age>\n</data>
复制代码
这种灵活性是 f-string 所不具备的,对于构建各种 DSL(领域特定语言)、模板引擎或格式化系统非常有价值。
Template 类的布局

t-string 求值后的 Template 类具有以下主要属性和方法:
  1. class Template:
  2.     strings: tuple[str, ...]
  3.     """
  4.     模板中字符串部分的非空元组。
  5.     包含 N+1 个元素,其中 N 是模板中插值表达式的数量。
  6.     """
  7.     interpolations: tuple[Interpolation, ...]
  8.     """
  9.     模板中插值部分的元组。
  10.     如果没有插值表达式,这将是一个空元组。
  11.     """
  12.     def __new__(cls, *args: str | Interpolation):
  13.         """
  14.         创建一个新的 Template 实例。
  15.         参数可以按任意顺序提供。
  16.         """
  17.         ...
  18.     @property
  19.     def values(self) -> tuple[object, ...]:
  20.         """
  21.         返回模板中每个 Interpolation 的 `value` 属性组成的元组。
  22.         如果没有插值表达式,这将是一个空元组。
  23.         """
  24.         ...
  25.     def __iter__(self) -> Iterator[str | Interpolation]:
  26.         """
  27.         迭代模板中的字符串部分和插值表达式。
  28.         这些可能以任意顺序出现。不包含空字符串。
  29.         """
  30.         ...
复制代码
这种布局使开发者能够:

  • 访问原始字符串片段(strings)
  • 访问插值表达式及其计算结果(interpolations)
  • 直接获取所有插值的值(values)
  • 按顺序迭代模板的所有组成部门
注:__iter__ 函数注释说出现顺序不固定,但 PEP 文档中它的详细实现却是按序的,我认为是注释有误。
t-string 与 f-string 的异同点

相似之处


  • 基本语法:二者都利用花括号 {} 作为插值表达式的分隔符
  • 表达式求值:都支持在花括号中放置任意 Python 表达式
  • 格式阐明符:都支持格式阐明符(如 .2f)和转换阐明符(如 !r)
  • 引号支持:都支持所有有效的引号标记('、"、'''、""")
  • 巨细写不敏感前缀:t 和 T 都是有效的,就像 f 和 F
不同之处


  • 返回范例:f-string 直接返回 str 范例,而 t-string 返回 Template 范例
  • 求值时机:f-string 在定义时立即求值,t-string 提供延迟求值本领
  • 布局访问:t-string 允许访问原始模板的布局(字符串部门和插值部门)
  • 处理模型:f-string 是"即时完成"模型,t-string 是"预处理+转换"模型
t-string 的应用场景

1. 安全的 HTML 模板

利用 t-string 可以创建出主动转义用户输入的 HTML 模板:
  1. def html(template: Template) -> str:
  2.     parts = []
  3.     for item in template:
  4.         if isinstance(item, str):
  5.             parts.append(item)
  6.         else:  # Interpolation
  7.             parts.append(html_escape(item.value))
  8.     return "".join(parts)
  9. user_input = ""
  10. safe_html = html(t"{user_input}")
  11. # 输出: <script>alert('XSS')</script>
复制代码
2. 安全的 SQL 查询构建

t-string 可以构建防注入的 SQL 查询:
  1. def safe_sql(template: Template) -> str:
  2.     parts = []
  3.     params = []
  4.    
  5.     for item in template:
  6.         if isinstance(item, str):
  7.             parts.append(item)
  8.         else:
  9.             parts.append("?")
  10.             params.append(item.value)
  11.    
  12.     return "".join(parts), params
  13. user_id = "user' OR 1=1--"
  14. query, params = safe_sql(t"SELECT * FROM users WHERE id = {user_id}")
  15. # query: "SELECT * FROM users WHERE id = ?"
  16. # params: ["user' OR 1=1--"]
复制代码
3. 布局化日志

利用 t-string 可以实现优雅的布局化日志记录:
  1. import json
  2. import logging
  3. from string.templatelib import Template, Interpolation
  4. class TemplateMessage:
  5.     def __init__(self, template: Template) -> None:
  6.         self.template = template
  7.    
  8.     @property
  9.     def message(self) -> str:
  10.         # 格式化为可读消息
  11.         return f(self.template)  # 使用自定义 f() 函数
  12.    
  13.     @property
  14.     def values(self) -> dict:
  15.         # 提取结构化数据
  16.         return {
  17.             item.expression: item.value
  18.             for item in self.template.interpolations
  19.         }
  20.    
  21.     def __str__(self) -> str:
  22.         return f"{self.message} >>> {json.dumps(self.values)}"
  23. action, amount, item = "traded", 42, "shrubs"
  24. logging.info(TemplateMessage(t"User {action}: {amount:.2f} {item}"))
  25. # 输出: User traded: 42.00 shrubs >>> {"action": "traded", "amount": 42, "item": "shrubs"}
复制代码
4. 安全的子进程调用

PEP-787 专门针对 t-string 在子进程调用中的场景作了扩展,使 subprocess 模块能原生支持 t-string:
  1. # 不安全的 f-string 用法
  2. subprocess.run(f"echo {message_from_user}", shell=True)  # 命令注入风险
  3. # 安全的 t-string 用法
  4. subprocess.run(t"echo {message_from_user}")  # 自动进行适当的命令转义
复制代码
这种方式既保留了字符串命令的可读性,又克制了安全风险。
t-string 的进阶用法

1. 自定义多功能模板渲染器

t-string 的真正威力在于可以自定义渲染器模板:
  1. from string.templatelib import Template, Interpolation
  2. import html
  3. def smart_renderer(template: Template, context="text") -> str:
  4.     """上下文感知的渲染器
  5.     根据context参数自动决定如何处理每个插值:
  6.     - "text": 普通文本模式,直接转为字符串
  7.     - "html": HTML模式,自动转义HTML特殊字符,防止XSS
  8.     - "sql": SQL模式,自动转义SQL特殊字符,防止注入
  9.     """
  10.     parts = []
  11.    
  12.     for item in template:
  13.         if isinstance(item, str):
  14.             parts.append(item)
  15.         else:  # Interpolation
  16.             value = item.value
  17.             expression = item.expression
  18.             
  19.             # 基于值类型和上下文进行智能处理
  20.             if context == "html":
  21.                 # HTML模式:自动转义HTML特殊字符
  22.                 parts.append(html.escape(str(value)))
  23.             elif context == "sql":
  24.                 # SQL模式:防止SQL注入
  25.                 if isinstance(value, str):
  26.                     # 将1个单引号转义成2个
  27.                     escaped_value = value.replace("'", "''")
  28.                     parts.append(f"'{escaped_value}'")
  29.                 elif value is None:
  30.                     parts.append("NULL")
  31.                 else:
  32.                     parts.append(str(value))
  33.             else:
  34.                 parts.append(str(value))
  35.    
  36.     return "".join(parts)
  37. # 同一个模板在不同上下文中的自动适配渲染
  38. user_input = ""
  39. template = t"用户输入: {user_input}"
  40. print(smart_renderer(template, context="html")) # 输出: 用户输入: <script>alert(&#x27;evil&#x27;)</script>
  41. # SQL注入防护示例
  42. user_id = "1'; DROP TABLE users; --"
  43. sql_template = t"SELECT * FROM users WHERE id = {user_id}"
  44. print(smart_renderer(sql_template, context="sql")) # 输出: SELECT * FROM users WHERE id = '1''; DROP TABLE users; --'
  45. # f-string 对于SQL注入,必须先处理值,再放入f-string
  46. escaped_id = user_id.replace("'", "''")
  47. sql_safe_id = f"'{escaped_id}'"
  48. print(f"SQL查询(f-string): SELECT * FROM users WHERE id = {sql_safe_id}")
复制代码
2. 布局化嵌套模板处理

t-string 和 f-string 在嵌套利用时有本质区别:
  1. # f-string的嵌套:内部表达式立即求值,信息丢失
  2. value = "world"
  3. inner_f = f"inner {value}"
  4. outer_f = f"outer {inner_f}"
  5. print(outer_f)  # 输出: outer inner world
  6. print(type(outer_f))  # <class 'str'> - 只是普通字符串
  7. # t-string的嵌套:保留完整结构信息
  8. inner_t = t"inner {value}"
  9. outer_t = t"outer {inner_t}"
  10. print(type(outer_t))  # <class 'string.templatelib.Template'>
  11. print(type(outer_t.interpolations[0].value))  # 也是Template对象!
  12. # 可以访问和处理任意深度的嵌套结构
  13. user = {"name": "Alice", "age": 30}
  14. message = t"用户{user['name']}信息: {t'年龄:{user['age']}'}"
  15. inner_template = message.interpolations[1].value
  16. print(inner_template.strings)  # 输出: ('年龄:', '')
  17. print(inner_template.interpolations[0].value)  # 输出: 30
复制代码
这种布局化处理本领使 t-string 特别恰当构建复杂的模板系统,可以按需延迟或自定义渲染过程的所有部门。
3. 延迟求值与异步处理

t-string 的布局特性使得它支持延迟求值和异步处理。以下是异步模板渲染示例:
  1. import asyncio
  2. # 模拟异步数据获取
  3. async def fetch_data(key: str) -> str:
  4.     await asyncio.sleep(0.1)  # 模拟网络延迟
  5.     return f"获取的{key}数据"
  6. async def render_async(template):
  7.     tasks = {}
  8.     # 并行启动所有异步查询
  9.     for item in template.interpolations:
  10.         tasks[item.expression] = asyncio.create_task(
  11.             fetch_data(item.expression)
  12.         )
  13.    
  14.     # 等待所有查询完成
  15.     for expr, task in tasks.items():
  16.         tasks[expr] = await task
  17.    
  18.     # 组装结果
  19.     result = []
  20.     for item in template:
  21.         if isinstance(item, str):
  22.             result.append(item)
  23.         else:
  24.             result.append(tasks[item.expression])
  25.    
  26.     return "".join(result)
  27. async def main():
  28.     template = t"用户: {user}, 年龄: {age}"
  29.     result = await render_async(template)
  30.     print(result)
  31. # asyncio.run(main())
复制代码
这种模式的关键上风:

  • 布局保留: 可以获取完整表达式信息
  • 并行获取: 同时处理多个异步任务
  • 延迟组合: 等所有数据就绪再拼接
总结

Python 的 t-string 语法是对字符串处理本领的紧张扩展,它在保持与 f-string 语法相似性的同时,提供了更灵活、更安全的字符串插值处理机制。通过将字符串模板布局化为 Template 对象,开发者可以在字符串组合前对插值举行拦截和转换,从而克制常见的安全问题,并支持更多高级用例。
它就像是数据与视图的分离模式,f-string 是直接渲染的视图,而 t-string则保留了数据模型,允许你在最终呈现前实行各种转换规则和验证。
t-string 的计划理念体现了功能性与安全性的均衡,虽然它比 f-string 更复杂,但这种复杂性带来了更高级的可组合性和更强的安全保障。
它依照了 Python 的"显式优于隐式"原则,通过明确分离模板布局和渲染过程,让字符串处理的每个步调都清晰可见。
t-string 并不是一种替换 f-string 的语法,f-string 的简单易用性依然有其紧张价值。
那么,在 Python 3.14 版本后,两个字符串插值方法该怎样选择呢?
一句话总结:当只需简单地格式化字符串时,利用 f-string 更直接高效;而当处理不受信任的输入、需要自定义渲染逻辑、构建复杂模板系统或举行异步处理时,应选择功能更强大的 t-string。
参考资料

Python 潮流周刊第3季总结,附电子书下载:https://pythoncat.top/posts/2025-04-20-sweekly
Python 潮流周刊第二季完结(31~60):https://pythoncat.top/posts/2025-04-20-iweekly
Python 潮流周刊第 2 季完结了,分享几项总结:https://pythoncat.top/posts/2024-07-14-iweekly
Python 潮流周刊第2季(31~60)-纯链接版:https://pythoncat.top/posts/2025-04-19-sweekly
Python 潮流周刊第一季英华合集(1~30):https://pythoncat.top/posts/2023-12-11-weekly
万字浓缩版,Python 潮流周刊第 1 季的 800 个链接!:https://xiaobot.net/post/78c3d645-86fa-4bd8-8eac-46fb192a339e
微信关注 Python猫https://img.pythoncat.top/python_cat.jpg

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南七星之家

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表