![[译]Python 和 TOML:新最好的朋友 (1) 了解TOML [译]Python 和 TOML:新最好的朋友 (1) 了解TOML](https://www.bmabk.com/wp-content/uploads/2022/05/post-loading.gif)
Python 和 TOML:新最好的朋友
原文:《Python and TOML: New Best Friends》[1]
![[译]Python 和 TOML:新最好的朋友 (1) 了解TOML [译]Python 和 TOML:新最好的朋友 (1) 了解TOML](https://www.bmabk.com/wp-content/uploads/2022/05/post-loading.gif)
TOML
[2](Tom’s Obvious Minimal Language)是一种相当新的配置文件格式。Python社区在过去几年中已经接受了它,许多流行的工具都使用TOML 进行配置,您将在构建和分发自己的包时可能就会使用 pyproject.toml
。
使用TOML作为配置文件
TOML最初目标是成为一种易于人类阅读和编写的配置文件格式。
之前,已经有了许多配置文件格式,如JSON、YAML、INI等。但是它们不是很适合人类读写。JSON多层嵌套时,很难阅读。YAML的缩进可能很混乱。INI没有同一的规范,且只适合简单的配置。–《https://zhuanlan.zhihu.com/p/50412485》
配置和配置文件
几乎任何程序都需要配置。例如,使用配置文件指定数据库信息。
为项目使用配置文件是将代码与其设置分开的好方法。
现在,考虑一个井字游戏的配置文件:
player_x_color = blue
player_o_color = green
board_size = 3
server_url = https://tictactoe.example.com/
您可以直接在源代码中对此进行编码。但通过单独的配置文件可以实现以下几点:
-
• 为值提供显式名称
-
• 为这些值提供更多的可见性
-
• 使值更容易更改
仔细观察这些配置,可能会发现这些配置需要分成不同部分:
color
信息可能是用户配置的,而board_size
可能是固定的,server_url
是服务器相关的。
一种选择是将配置分成多个文件,另一种是进行分组:
[user]
player_x_color = blue
player_o_color = green
[constant]
board_size = 3
[server]
url = https://tictactoe.example.com
文件的组织使每个配置项的角色更加清晰。您还可以向配置文件添加注释使得配置项更更加清晰。
TOML: Tom’s Obvious Minimal Language
TOML是一种相当新的格式。第一个格式规范 0.1.0 版于 2013
年发布。从一开始,它就专注于成为人类可读的最小配置文件格式。TOML文档描述其目标如下:
TOML旨在成为一种最小的配置文件格式,语义明显,易于阅读。TOML 旨在明确映射到哈希表。TOML应该很容易解析为各种语言的数据结构。
TOML非常流行,越来越多的 Python 工具(包括 Black、pytest、mypy 和 isort)使用 TOML 进行配置。
回想一下上一小节中的配置。
在TOML中表达它的方式如下:
[user]
player_x.color = "blue"
player_o.color = "green"
[constant]
board_size = 3
[server]
url = "https://tictactoe.example.com"
TOML 模式验证
在实践中,给定的 TOML 文件也可能带有一些非语法要求。这些是架构要求(schema requirements)。例如,井字游戏应用程序可能要求配置文件包含server URL。另一方面,player color可能是可选的,因为应用程序定义了默认颜色。
目前,TOML不包含可以在TOML文档中指定必需和可选字段的架构语言。存在几个提案,尽管目前尚不清楚是否会很快接受其中任何一个。
在简单的应用程序中,您可以手动验证 TOML 配置。例如,您可以使用 Python 3.10 中引入的结构模式匹配(Structural Pattern Matching)。假设您已将配置解析为 Python 并将其命名为 config 。然后,您可以按如下方式检查其结构:
match config:
case {
"user": {"player_x": {"color": str()}, "player_o": {"color": str()}},
"constant": {"board_size": int()},
"server": {"url": str()},
}:
pass
case _:
raise ValueError(f"invalid configuration: {config}")
第一个 case 语句阐明了您期望的结构。如果 config 匹配,则用于 pass 继续代码。否则,将引发错误。
如果您的 TOML 文档更复杂,则此方法可能无法很好地扩展。如果你想提供良好的错误消息,你还需要做更多的工作。更好的选择是使用 pydantic
,它利用类型注释在运行时进行数据验证。pydantic 的一个优点是它内置了精确且有用的错误消息。
还有一些工具可以利用 JSON 等格式的现有架构验证。例如,Taplo
是一个 TOML 工具包,可以根据 JSON 模式验证 TOML 文档。Taplo 也可用于 Visual Studio Code,捆绑在 Even Better TOML
扩展中。
了解TOML:键值对
TOML 是围绕键值对构建的,这些键值对很好地映射到哈希表数据结构。TOML 值具有不同的类型。每个值可以使以下类型之一:
-
• 字符串
-
• 整数
-
• 浮点数
-
• 布尔值
-
• 日期和时间
-
• 数组
-
• 内联表
此外,还可以将table 和array of tables作为集合来组织多个键值对。您将在本节的其余部分了解有关所有这些内容的更多信息,以及如何在 TOML 中指定它们。
注:TOML 支持与 Python 语法相同的注释(
#
)。
如前所述,键值对是 TOML 文档中的基本构建块。您可以使用 <key> = <value>
语法:
greeting = "Hello, TOML!"
在此示例中, greeting
是键,而 "Hello, TOML!"
是值。值具有类型。在此示例中,该值是一个文本字符串。键始终被解释为字符串,即使不用引号括起来也是如此。请看以下示例:
greeting = "Hello, TOML!"
42 = "Life, the universe, and everything"
此处42是一个 有效的键,它被解释为字符串,而不是数字。通常,您希望使用裸键。这些键仅由 ASCII 字母和数字以及下划线和破折号组成。所有这些键都可以不带引号地编写,如上面的示例所示。
TOML 文档必须以 UTF-8 Unicode 编码。这为您提供了极大的灵活性,可以代表各种值。
您也可以在键中使用 Unicode。若要使用 Unicode 键,必须在它们两边添加引号:
"realpython.com" = "Real Python"
"blåbærsyltetøy" = "blueberry jam"
"Tom Preston-Werner" = "creator"
点(.
)在 TOML 键中起着特殊作用。您可以在不带引号的键中使用点,在这种情况下,它们将通过拆分每个点的点键来触发分组:
player_x.symbol = "X"
player_x.color = "purple"
在这里,您指定两个点键(dotted key)。由于它们都以 player_x 开头,因此键 symbol 和 color 将组合在名为 player_x 的部分中。当您开始浏览表时,您将了解有关点键的更多信息。
接下来,将注意力转向值。在下一节中,您将了解 TOML 中最基本的数据类型。
Strings, Numbers, and Booleans
TOML 对基本数据类型使用熟悉的语法。来自Python的您能够识别字符串、整数、浮点数和布尔值:
string = "Text with quotes"
integer = 42
float = 3.11
boolean = true
TOML 和 Python 之间的直接区别在于 TOML 的布尔值是小写的:
true
和false
。
TOML 字符串通常应使用双引号,并可以用反斜杠()转义字符。还可以使用单引号指定字符串。单引号字符串称为文字字符串,其行为类似于 Python 中的原始字符串。最后,还可以使用三引号(
"""
或 '''
)指定字符串。三引号字符串允许您在多行上编写字符串,类似于 Python 多行字符串:
partly_zen = """
Flat is better than nested.
Sparse is better than dense.
"""
TOML 中的数字可以是整数,也可以是浮点数。整数表示整数,并指定为纯数字字符。与 Python 一样,您可以使用下划线来增强可读性:
number = 42
negative = -8
large = 60_481_729
Tables
您已经了解了 TOML 文档由一个或多个键值对组成。当用编程语言表示时,它们应存储在哈希表数据结构中。在 Python 中,这将是一个字典或其他类似字典的数据结构。要组织键值对,您可以使用表。
TOML 支持三种不同的表指定方式。
-
• 在大多数情况下,使用带有标头的常规表。
-
• 当您需要指定几个与其父表密切相关的键值对时,请使用点键表。
-
• 仅对具有最多三个键值对的非常小的表使用内联表,其中数据构成明确定义的实体。
[user]
player_x.color = "blue"
player_o.color = "green"
[constant]
board_size = 3
[server]
url = "https://tictactoe.example.com"
在此示例中,您有三个表:user、constant 和 server。每个表都有一个标头,用方括号括起来。
您还可以在上面的配置中找到点键表(dotted key tables)。在 user
中:
[user]
player_x.color = "blue"
player_o.color = "green"
键中的点 (.)创建一个由点之前的键部分命名的表(player_x)。您还可以通过嵌套常规表实现相同效果:
[user]
[user.player_x]
color = "blue"
[user.player_o]
color = "green"
缩进在 TOML 中并不重要。您可以在此处使用它来表示表的嵌套。您可以看到该 user 表包含两个子表, player_x
以及 player_o
。每个子表都包含一个键值对。
请注意,您需要在嵌套表的标头中使用点键,并命名所有中间表。这使得 TOML 标头规范非常冗长。在类似的规范中,例如 JSON 或 YAML,您只需指定子表名称,而不重复外部表的名称。同时,这使得 TOML 非常明确,并且更难在深度嵌套的结构中迷失方向。
现在,您将通过为每个玩家添加标签或符号来扩展 user 表格。您将以三种不同的形式表示此表,首先仅使用常规表,然后使用点键表,最后使用内联表。
常规表:
[user]
[user.player_x]
symbol = "X"
color = "blue"
[user.player_o]
symbol = "O"
color = "green"
点键表:
[user]
player_x.symbol = "X"
player_x.color = "blue"
player_o.symbol = "O"
player_o.color = "green"
这比上面的嵌套表更短、更简洁。但是,结构现在不太清晰。
内联表:
[user]
player_x = { symbol = "X", color = "blue" }
player_o = { symbol = "O", color = "green" }
内联表使用大括号 {}
定义,这些大括号 用逗号分隔的键值对换行。在此示例中,内联表在可读性和紧凑性之间取得了很好的平衡,因为玩家表的分组变得清晰。
TOML 文档由一个无名根表(root table)表示,该根表包含所有其他表和键值对。在 TOML 配置顶部(在任何表头之前)写入的键值对直接存储在根表中:
title = "Tic-Tac-Toe"
[constant]
board_size = 3
在此示例中, title
是根表中的键, constant
是嵌套在根表中的表,board_size
是constant
表中的键。
请注意,表包含在其标头和下一个表标头之间写入的所有键值对。在实践中,这意味着您必须在属于该表的键值对下方定义嵌套子表。请考虑以下文档:
[user]
[user.player_x]
color = "blue"
[user.player_o]
color = "green"
background_color = "white"
(看上去)缩进表明这应该 background_color
是 user
表中的键。但事实上,TOML 会忽略缩进,只检查表头。在此示例中, background_color
是 user.player_o
表的一部分。要更正此问题, background_color
应在嵌套表之前定义:
[user]
background_color = "white"
[user.player_x]
color = "blue"
[user.player_o]
color = "green"
Times and Dates
TOML 有四种日期和时间类型:
-
• offset date-time:偏移日期时间是带有时区信息的时间戳,表示特定时刻。
-
• local date-time:本地日期时间是不带时区信息的时间戳。
-
• local date :没有时区信息的日期
-
• local time:时间
TOML 基于 RFC 3339 表示时间和日期。本文档定义了一种时间和日期格式,该格式通常用于表示 Internet 上的时间戳。完全定义的时间戳如下所示: 2021-01-12T01:23:45.654321+01:00
。时间戳由多个字段组成,由不同的分隔符分隔。
2021-01-12T01:23:45.654321+01:00
对应的含义为: 年-月-日T时:分:秒.微秒+时区与UTC的偏移量
其中T可以用空格替换,微秒是可选的,偏移量可用Z表示与UTC时间相同。
Arrays
TOML数组是值的有序列表,可用[]
创建,类似Python的列表。
packages = ["tomllib", "tomli", "tomli_w", "tomlkit"]
players = [
{ symbol = "X", color = "blue", ai = true },
{ symbol = "O", color = "green", ai = false },
]
您可以将内联表放在方括号内。但是内联表不能很好地扩展。如果要表示表较大的表数组,则可以考虑表数组:
[[players]]
symbol = "X"
color = "blue"
ai = true
[[players]]
symbol = "O"
color = "green"
ai = false
此表数组等效于您上面编写的内联表数组。方括号双括号定义表数组,而不是常规表。
对于更广泛的示例,请考虑以下摘录自 TOML 文档,其中列出了测验应用程序的问题:
[python]
label = "Python"
[[python.questions]]
question = "Which built-in function can get information from the user"
answers = ["input"]
alternatives = ["get", "print", "write"]
[[python.questions]]
question = "What's the purpose of the built-in zip() function"
answers = ["To iterate over two or more sequences at the same time"]
alternatives = [
"To combine several strings into one",
"To compress several files into one archive",
"To get information from the user",
]
此例中,python表包含两个键:label
和 questions
。questions
键是一个表数组,其中每个元素都是一个表。每个questions
表都包含question
, answers
和 alternatives
。
引用链接
[1]
《Python and TOML: New Best Friends》: https://realpython.com/python-toml/[2]
TOML
: https://toml.io/cn/
原文始发于微信公众号(一只大鸽子):[译]Python 和 TOML:新最好的朋友 (1) 了解TOML
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/237865.html