找回密码
 立即注册
首页 群组 IT互联网 网站 程序园子 PEP750 模板字符串 t-string 中文翻译

PEP750 模板字符串 t-string 中文翻译

空娅芬 2025-5-29 18:16:58
PEP 750 在前些日正式被通过,鉴于截止本文发出 CPython 3.14 仍未包含相关实现,故本文仅对 PEP 750 进行翻译。
摘要

本提案引入了模板字符串(Template Strings),用于自定义字符串处理。模板字符串是 f-strings 的泛化形式,使用 t 前缀代替f。与 f-strings 直接生成字符串不同,t-strings 生成一个新的 Template 类型,允许开发者在值被组合前访问原始字符串及其插值内容。这为Python带来了原生的灵活字符串处理能力,支持安全检查、网页模板、领域特定语言等场景。
  1. template: Template = t"Hello {name}"
复制代码
与其他 Python 增强提案的关系

Python 在 Python 3.6 中通过 PEP 498 引入了 f-strings 。随后在 PEP 701 中对语法进行了正式化,并放宽了一些限制。本 PEP 基于 PEP 701 。
在 PEP 498 出现的同时,PEP 501 被撰写以提供“i-strings”——即,“插值模板字符串”。该提案由于对 f-strings 的进一步经验而被推迟。该提案的工作于 2023 年 3 月由不同的作者恢复,引入了“t-strings”作为模板字面量字符串,并基于 PEP 701 构建。
动机

Python f-strings 易于使用且非常受欢迎。然而,随着时间的推移,开发者发现它不适合某些用途。特别地,f-strings 并未提供了一种在最终字符串组合之前拦截和转换插值值的方法。
因此,不恰当地使用 f-strings 可能会导致安全漏洞。例如,用户执行 SQL 查询时,可能会尝试使用 f-strings 将值嵌入到 SQL 表达式中,这可能会导致 SQL 注入攻击。或者,开发人员在构建 HTML 时,可能会包含未转义的用户输入,从而导致跨站脚本 (XSS) 漏洞。更广泛地说,无法在将插值值组合到最终字符串之前对其进行转换,导致 f-strings 无法在更复杂的字符串处理任务中使用。
模板字符串通过为开发者提供对字符串及其插值的访问权限来解决这些问题。
例如,假设我们想要生成一些 HTML。使用模板字符串,我们可以定义一个 html() 函数,允许我们自动处理内容:
  1. evil = ""
  2. template = t"<p>{evil}</p>"
  3. assert html(template) == "<p><script>alert('evil')</script></p>"
复制代码
同样,我们假设的 html() 函数可以让开发者使用字典轻松地为 HTML 元素添加属性:
  1. attributes = {"src": "https://www.cnblogs.com/shrubbery.jpg", "alt": "looks nice"}
  2. template = t"<img {attributes} />"
  3. assert html(template) == '<img src="https://www.cnblogs.com/shrubbery.jpg" alt="looks nice" />'
复制代码
这两个示例都无法使用 f-strings 实现。
规范

模板字符串字面量

本提案引入了一个新的字符串前缀 t ,用于定义模板字符串字面量。这些字面量解析为 Template 类型。可以从标准库 string.templatelib 模块找到该类型。
下面是一个示例:
  1. from string.templatelib import Template
  2. template = t"This is a template string."
  3. assert isinstance(template, Template)
复制代码
模板字符串字面量支持 PEP 701 的完整语法。这包括能够在插值中嵌套模板字符串的能力,以及使用所有有效的引号( ' 、 " 、 ''' 和 """ )。与其他字符串前缀一样, t 前缀必须紧接在引号之前。与 f-strings 一样,t-strings 同时支持小写 t 和大写 T 前缀。与 f-strings 一样,t-strings 不能与 ub 前缀结合使用。
此外,f-stringst-strings 不能结合使用,因此 ft 前缀无效。t-strings 可以与 r 前缀结合;有关更多信息,请参见原始模板字符串部分。
模板类型

string.templatelib.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.         创建一个新的模板实例。
  15.         参数可以以任何顺序提供。
  16.         """
  17.         ...
  18.     @property
  19.     def values(self) -> tuple[object, ...]:
  20.         """
  21.         返回一个元组,包含模板中每个插值的`value`属性
  22.         如果没有插值,这将是一个空元组。
  23.         """
  24.         ...
  25.     def __iter__(self) -> Iterator[str | Interpolation]:
  26.         """
  27.         遍历模板中的字符串部分和插值。
  28.         它们可能以任何顺序出现,不包含空字符串。
  29.         """
  30.         ...
复制代码
strings 和 interpolations 属性分别提供了对字符串部分和插值部分的访问:
  1. name = "World"
  2. template = t"Hello {name}"
  3. assert template.strings[0] == "Hello "
  4. assert template.interpolations[0].value == "World"
复制代码
插值类型

string.templatelib.Interpolation
插值类型表示模板字符串中的插值。
  1. class Interpolation:
  2.     value: object
  3.     expression: str
  4.     conversion: Literal["a", "r", "s"] | None
  5.     format_spec: str
  6.     __match_args__ = ("value", "expression", "conversion", "format_spec")
  7.     def __new__(
  8.         cls,
  9.         value: object,
  10.         expression: str,
  11.         conversion: Literal["a", "r", "s"] | None = None,
  12.         format_spec: str = "",
  13.     ):
  14.         ...
复制代码
插值类型是浅不可变的,其属性无法重新赋值。
value 属性是插值的计算结果,expression 属性是插值的原始表达式。
  1. name = "World"
  2. template = t"Hello {name}"
  3. assert template.interpolations[0].value == "World"
  4. assert template.interpolations[0].expression == "name"
复制代码
我们预计在大多数模板处理代码中不会使用 expression 属性。该属性是为了完整性以及在调试和反向工程中使用而提供的。有关如何处理模板字符串的更多信息,请参阅“常见在处理模板中看到的模式”部分和“示例”部分。
conversion 属性用于可选的类型转换,可以是 rsa ,分别对应 repr() 、 str() 和 ascii() 转换。与 f-strings 相同,不支持其他转换。若未指定转换,则值为 None 。
  1. name = "World"
  2. template = t"Hello {name!r}"
  3. assert template.interpolations[0].conversion == "r"
复制代码
format_spec 属性指定格式规范。就像 f-strings 一样,这是一个字符串,定义了如何呈现值。若未指定格式,则值为空字符串。
  1. value = 42
  2. template = t"Value: {value:.2f}"
  3. assert template.interpolations[0].format_spec == ".2f"
复制代码
格式规范在 f-strings 中可以包含内插,这在模板字符串中也是允许的。
  1. value = 42
  2. precision = 2
  3. template = t"Value: {value:.{precision}f}"
  4. assert template.interpolations[0].format_spec == ".2f"
复制代码
f-strings 不同,处理模板的代码需要自行决定如何解释 conversion 和 format_spec 属性。这样的代码不需要使用这些属性,但当这些属性存在时,应该被尊重,并尽可能匹配 f-strings 的行为。例如,如果模板字符串使用了 {value:.2f} ,在处理时没有将值舍入到两位小数,这将是令人惊讶的。
模板类型 values 属性

Template.values 属性是访问模板中每个 Interpolation 的 value 属性的快捷方式,并等价于:
  1. @property
  2. def values(self) -> tuple[object, ...]:
  3.     return tuple(i.value for i in self.interpolations)
复制代码
迭代模板类型内容

Template.__iter__() 方法提供了一种简单的方式来访问模板的完整内容。它按出现顺序提供字符串部分和插值,省略空字符串
  1. assert list(t"") == []
  2. assert list(t"Hello") == ["Hello"]
  3. name = "World"
  4. template = t"Hello {name}!"
  5. contents = list(template)
  6. assert len(contents) == 3
  7. assert contents[0] == "Hello "
  8. assert contents[1].value == "World"
  9. assert contents[1].expression == "name"
  10. assert contents[2] == "!"
复制代码
  1. first = "Eat"
  2. second = "Red Leicester"
  3. template = t"{first}{second}"
  4. contents = list(template)
  5. assert len(contents) == 2
  6. assert contents[0].value == "Eat"
  7. assert contents[0].expression == "first"
  8. assert contents[1].value == "Red Leicester"
  9. assert contents[1].expression == "second"
  10. # However, the strings attribute contains empty strings:
  11. assert template.strings == ("", "", "")
复制代码
处理模板字符串

开发者可以自行编写代码,处理模板字符串。例如,以下函数将模板的字符串部分转换为小写,将插值转换为大写:
  1. from string.templatelib import Template, Interpolation
  2. def lower_upper(template: Template) -> str:
  3.     """Render static parts lowercased and interpolations uppercased."""
  4.     parts: list[str] = []
  5.     for item in template:
  6.         if isinstance(item, Interpolation):
  7.             parts.append(str(item.value).upper())
  8.         else:
  9.             parts.append(item.lower())
  10.     return "".join(parts)
  11. name = "world"
  12. assert lower_upper(t"HELLO {name}") == "hello WORLD"
复制代码
模板字符串不需要以任何特定方式处理。处理模板的代码无需返回字符串。模板字符串是一种灵活的一般用途功能。
参见处理模板部分中的常见模式,以获取更多关于处理模板字符串的信息。参见示例部分以获取详细的示例。
模板字符串连接

模板字符串支持使用+显式连接 。连接支持两个 Template 实例,或一个 Template 实例和一个字符串 :
  1. name = "World"
  2. template = t"{name}"
  3. assert isinstance(t"Hello " + template, Template)
  4. assert (t"Hello " + template).strings == ("Hello ", "")
  5. assert (t"Hello " + template).interpolations[0].value == "World"
  6. assert isinstance("Hello " + template, Template)
  7. assert ("Hello " + template).strings == ("Hello ", "")
  8. assert ("Hello " + template).interpolations[0].value == "World"
复制代码
Template 和字符串的连接总是生成一个 Template 实例。
模板字符串同时支持隐式连接:
  1. name = "World"
  2. assert (t"Hello " t"World").strings == ("Hello World",)
  3. assert ("Hello " t"World").strings == ("Hello World",)
复制代码
模板字符串对两个 Template 及 Template 与字符串间实现了 __add__() 与 __radd__() 方法。
模板与插值的相等性

Template 和 Interpolation 实例通过比较引用对象是否为同一个(即 is)判断是否相等。
Template 实例旨在被模板处理代码使用,后者可能返回一个字符串或其他任何类型。这些类型可以根据需要提供自己的相等性语义。
不支持排序

Template 和 Interpolation 类型均不支持排序。这与 Python 中所有其他字符串字面量类型不同(它们支持字典序排序)。由于插值可能包含任意值,因此它们没有自然排序方式。因此, Template 和 Interpolation 类型都没有实现标准的比较方法。
支持调试标示符

调试标记符 = 在模板字符串中被支持,并且行为类似于在 f-strings 中的行为,但由于实现的限制,存在细微差别。
特别地, t'{value=}' 被视为 t'value={value!r}' :
  1. name = "World"
  2. template = t"Hello {name=}"
  3. assert template.strings[0] == "Hello name="
  4. assert template.interpolations[0].value == "World"
  5. assert template.interpolations[0].conversion == "r"
复制代码
如果还提供了单独的格式字符串, t'{value=:fmt} 将被视为 t'value={value!s:fmt}' 。在调试标识符中空白会被保留,因此 t'{value = }' 将被视为 t'value = {value!r}' 。
未完待续

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
1.jpg