[译]Python 和 TOML:新最好的朋友 (1) 了解TOML

[译]Python 和 TOML:新最好的朋友 (1) 了解TOML

Python 和 TOML:新最好的朋友

原文:《Python and TOML: New Best Friends》[1]

[译]Python 和 TOML:新最好的朋友 (1) 了解TOML
toml

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_sizeconstant表中的键。

请注意,表包含在其标头和下一个表标头之间写入的所有键值对。在实践中,这意味着您必须在属于该表的键值对下方定义嵌套子表。请考虑以下文档:

[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 和 questionsquestions 键是一个表数组,其中每个元素都是一个表。每个questions 表都包含questionanswers 和 alternatives

引用链接

[1] 《Python and TOML: New Best Friends》: https://realpython.com/python-toml/
[2] TOMLhttps://toml.io/cn/


原文始发于微信公众号(一只大鸽子):[译]Python 和 TOML:新最好的朋友 (1) 了解TOML

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/237865.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!