我有想法创建一个 TOML 配置文件,它定义某种要执行的作业链。这些作业不会立即执行,中间可能会有中断,因此还应该进行一些分组,以便有机会在链之间执行某些操作。
TOML 文件示例如下所示:
[jobid_3.2]
name='script_3.2'
type='sql'
[jobid_10.1]
name='whatever_works'
type='shell'
[jobid_1.1]
name='script_1.1'
type='shell'
[jobid_3.1]
name='foobar'
type='shell'
[jobid_2.1]
name='barbaz'
type='shell'
[jobid_2.2]
name='script_2.2'
type='sql'
所以,顺序有点乱,因此配置读取应该处于某种有序状态。
问题 1:将 TOML 字符串读入变量时,最终会得到嵌套字典,在本例中为 3(jobid_xxx、y,然后是名称类型的字典)
问题 2:TOML 将整数识别为字符串,而不是用作表数组时的整数。这意味着我们必须在第一个嵌套字典中进行一些转换。
问题 3:第一级排序应该发生在数字上,而不是字符串本身。当然,我可以使用
[3.2]
代替 [jobid_3.2]
,但这实际上看起来更好,更具描述性。
那么,如何解决这个问题?
我花了一些时间才明白这是如何运作的,经历了很多尝试和失败。但是,工作解决方案如下所示:
import tomli
from collections import OrderedDict
string = """[jobid_3.2]
name='script_3.2'
type='sql'
[jobid_10.1]
name='whatever_works'
type='shell'
[jobid_1.1]
name='script_1.1'
type='shell'
[jobid_3.1]
name='foobar'
type='shell'
[jobid_2.1]
name='barbaz'
type='shell'
[jobid_2.2]
name='script_2.2'
type='sql'
"""
t = tomli.loads(string)
scripts = OrderedDict(
sorted(
{
k_outer: dict(
sorted(
{
int(k_inner): v_inner for k_inner, v_inner in v_outer.items()
}.items()
)
)
for k_outer, v_outer in t.items()
}.items(),
key=lambda x: int(re.match("^.+_(\d+)", x[0]).group(1)),
)
)
print(scripts)
输出:
OrderedDict([('jobid_1', {1: {'name': 'script_1.1', 'type': 'shell'}}),
('jobid_2',
{1: {'name': 'barbaz', 'type': 'shell'},
2: {'name': 'script_2.2', 'type': 'sql'}}),
('jobid_3',
{1: {'name': 'foobar', 'type': 'shell'},
2: {'name': 'script_3.2', 'type': 'sql'}}),
('jobid_10', {1: {'name': 'whatever_works', 'type': 'shell'}})])
看起来确实很复杂,我花了半天时间才弄清楚,但效果很好。
最内部的排序是针对第 2 层的字典,我们也希望将数字字符串转换为普通整数。因此我们必须从父字典(v_outer)中获取值。外面的
sorted
最终是在第一层进行整体排序。我们使用正则表达式并将匹配转换为整数,这样我们得到 1,2,3,4,..., 10 而不是 1,10,2,3,4,...
作为替代方案,您可以使用
[3.2]
作为表格标记并省略尾随字符串 jobid_
,因此您可能需要稍微更改一下代码:
scripts = OrderedDict(
sorted(
{
int(k_outer): dict(
sorted(
{int(k_inner): v_inner for k_inner, v_inner in v_outer.items()}.items()
)
) for k_outer, v_outer in t.items()
}.items()
)
)
您可以将密钥放在外部
sorted
,但需要将字符串转换为整数。
如果有更简单的方法,请在这里分享,我会很感兴趣!