模板字符串,也被称为 t-string,已被正式接纳为 Python 3.14 的一项特性,该版本将于 2025 年底发布。🎉
我对此感到兴奋;t-string为 Python 中更安全、更灵活的字符串处理打开了大门。
t-string的核心思想是什么?
自 Python 3.6 引入以来,f-string已成为一种非常流行的字符串格式化方式。它们简洁、可读且功能强大。
事实上,它们非常方便,以至于许多开发者将 f-string用于一切... 即使在不应该使用它们的时候!
然而,f-string经常被危险地(误)用于格式化包含用户输入的字符串。我曾见过 f-string被用于 SQL (f"SELECT * FROM users WHERE name = '{user_name}'"
) 和 HTML (f"<div>{user_name}</div>"
)。这些做法不安全!如果user_name
包含恶意值,可能导致 SQL 注入或跨站脚本攻击(XSS)。
模板字符串是 Python 的 f-string的泛化。f-string会立即变成一个字符串,而 t-string会解析为一个新的类型,string.templatelib.Template
:
from string.templatelib import Template
name = "World"
template: Template = t"Hello {name}!"
重要的是,Template 实例并非字符串。Template 类型不提供其自身的__str__()
实现,也就是说,调用str(my_template)
不会返回有用的值。模板在使用前必须经过处理;这个处理代码可以由开发者编写,也可以由库提供,并且可以安全地转义动态内容。
我们可以想象一个库提供一个html()
函数,它接受一个 Template 并返回一个安全转义后的字符串:
evil = "<script>alert('bad')</script>"
template = t"<p>{evil}</p>"
safe = html(template)
assert safe == "<p><script>alert('bad')</script></p>"
当然,t-string的用途不仅仅是安全;它们还允许更灵活的字符串处理。例如,html()
函数可以返回一个新的类型,HTMLElement
。它还可以接受 HTML 本身中各种有用的替换项:
attributes = {"src": "roquefort.jpg", "alt": "Yum"}
template = t"<img {attributes} />"
element = html(template)
assert str(element) == "<img src='roquefort.jpg' alt='Yum' />"
如果你使用过 JavaScript,t-string可能会让你感到熟悉。它们是 JavaScript 标签模板 (tagged templates) 的 Pythonic 对应物。
如何使用 t-string?
为了支持处理,Template 提供了对其字符串部分和插值值(在它们组合成最终字符串之前)的访问权限。
Template 的.strings
和.values
属性返回元组:
name = "World"
template = t"Hello {name}!"
assert template.strings == ("Hello ", "!")
assert template.values == (name,)
字符串的数量总是比值的数量多一个(字符串可能为空)。也就是说,t"".strings == ("",)
和t"{name}".strings == ("", "")
。
作为一种快捷方式,Template 也可以被迭代:
name = "World"
template = t"Hello {name}!"
contents = list(template)
assert contents[0] == "Hello "
assert contents[1].value == name
assert contents[2] == "!"
编写复杂处理代码的开发者还可以访问每个插值的详细细节:
name = "World"
template = t"Hello {name!s:>8}!"
assert template.interpolations[0].value == name
assert template.interpolations[0].expression == "name"
assert template.interpolations[0].conversion == "s"
assert template.interpolations[0].format_spec == ">8"
除了支持字面形式 (t"foo"
),Template 也可以直接实例化:
from string.templatelib import Template, Interpolation
template = Template(
"Hello ",
Interpolation(value="World", expression="name"),
"!"
)
字符串和插值项可以以任何顺序提供给 Template 构造函数。
一个简单的 t-string示例
假设我们想编写代码将所有被替换的词转换为猪拉丁语。只需一个简单的函数即可:
def pig_latin(template: Template) -> str:
"""Convert a Template to pig latin."""
result = []
for item in template:
if isinstance(item, str):
result.append(item)
else:
word = item.value
if word and word[0] in "aeiou":
result.append(word + "yay")
else:
result.append(word[1:] + word[0] + "ay")
return "".join(result)
name = "world"
template = t"Hello {name}!"
assert pig_latin(template) == "Hello orldway!"
这是一个简单的例子;如果你想看一些不那么傻的例子,请查看PEP 750 示例仓库。
t-string发布后会发生什么?
t-string是一项强大的新特性,将使 Python 字符串处理更加安全和灵活。我希望看到它们在各种库和框架中使用,尤其是那些处理用户输入的。
此外,我希望工具生态系统能够适应并支持 t-string。例如,我希望看到 black 和 ruff 格式化 t-string的内容,并且 vscode 对这些内容进行语法高亮,如果它们是常见的类型,如 HTML 或 SQL。
很高兴能在这个项目上与 Jim、Paul、Koudai、Lysandros 和 Guido 认识并合作,并与 Python 社区的许多其他成员在线互动,没有他们的意见,PEP 750 根本无法成型。我迫不及待地想看到开发者们在 t-string发布后会构建出什么!
原文始发于微信公众号(知机安全):Python 的新型 t-string
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论