如何使用PyYAML读取python元组?

Aad*_*hah 14 python yaml pyyaml

我有以下YAML文件命名input.yaml:

cities:
  1: [0,0]
  2: [4,0]
  3: [0,4]
  4: [4,4]
  5: [2,2]
  6: [6,2]
highways:
  - [1,2]
  - [1,3]
  - [1,5]
  - [2,4]
  - [3,4]
  - [5,4]
start: 1
end: 4
Run Code Online (Sandbox Code Playgroud)

我正在使用PyYAML加载它并打印结果如下:

import yaml

f = open("input.yaml", "r")
data = yaml.load(f)
f.close()

print(data)
Run Code Online (Sandbox Code Playgroud)

结果是以下数据结构:

{ 'cities': { 1: [0, 0]
            , 2: [4, 0]
            , 3: [0, 4]
            , 4: [4, 4]
            , 5: [2, 2]
            , 6: [6, 2]
            }
, 'highways': [ [1, 2]
              , [1, 3]
              , [1, 5]
              , [2, 4]
              , [3, 4]
              , [5, 4]
              ]
, 'start': 1
, 'end': 4
}
Run Code Online (Sandbox Code Playgroud)

如您所见,每个城市和高速公路都表示为一个列表.但是,我希望它们被表示为元组.因此,我使用理解手动将它们转换为元组:

import yaml

f = open("input.yaml", "r")
data = yaml.load(f)
f.close()

data["cities"] = {k: tuple(v) for k, v in data["cities"].items()}
data["highways"] = [tuple(v) for v in data["highways"]]

print(data)
Run Code Online (Sandbox Code Playgroud)

然而,这似乎是一个黑客.有没有办法指示PyYAML直接将它们作为元组而不是列表读取?

idj*_*jaw 14

我不会打电话给你所做的事情.根据我的理解,您的替代方法是在YAML文件中使用特定于python的标记,以便在加载yaml文件时适当地表示它.但是,这需要你修改你的yaml文件,如果这个文件很大,可能会非常烦人而且不理想.

看看进一步说明这一点的PyYaml文档.最终,您希望!!python/tuple在您想要表示的结构前面放置一个.要获取您的样本数据,它希望:

YAML文件:

cities:
  1: !!python/tuple [0,0]
  2: !!python/tuple [4,0]
  3: !!python/tuple [0,4]
  4: !!python/tuple [4,4]
  5: !!python/tuple [2,2]
  6: !!python/tuple [6,2]
highways:
  - !!python/tuple [1,2]
  - !!python/tuple [1,3]
  - !!python/tuple [1,5]
  - !!python/tuple [2,4]
  - !!python/tuple [3,4]
  - !!python/tuple [5,4]
start: 1
end: 4
Run Code Online (Sandbox Code Playgroud)

示例代码:

import yaml

with open('y.yaml') as f:
    d = yaml.load(f.read())

print(d)
Run Code Online (Sandbox Code Playgroud)

哪个会输出:

{'cities': {1: (0, 0), 2: (4, 0), 3: (0, 4), 4: (4, 4), 5: (2, 2), 6: (6, 2)}, 'start': 1, 'end': 4, 'highways': [(1, 2), (1, 3), (1, 5), (2, 4), (3, 4), (5, 4)]}
Run Code Online (Sandbox Code Playgroud)

  • 带有标签的IMO最大的问题是,它们迫使你通过`load()`使用不安全的`Constructor`,并且不能再通过`safe_load()`使用`SafeConstructor`. (2认同)

Ant*_*hon 6

根据您的 YAML 输入来自“hack”的位置,这是一个很好的解决方案,特别是如果您使用yaml.safe_load()而不是不安全的yaml.load(). 如果 YAML 文件中的“叶子”序列需要是元组,则可以执行 \xc2\xb9:

\n\n
import pprint\nimport ruamel.yaml\nfrom ruamel.yaml.constructor import SafeConstructor\n\n\ndef construct_yaml_tuple(self, node):\n    seq = self.construct_sequence(node)\n    # only make "leaf sequences" into tuples, you can add dict \n    # and other types as necessary\n    if seq and isinstance(seq[0], (list, tuple)):\n        return seq\n    return tuple(seq)\n\nSafeConstructor.add_constructor(\n    u\'tag:yaml.org,2002:seq\',\n    construct_yaml_tuple)\n\nwith open(\'input.yaml\') as fp:\n    data = ruamel.yaml.safe_load(fp)\npprint.pprint(data, width=24)\n
Run Code Online (Sandbox Code Playgroud)\n\n

打印:

\n\n
{\'cities\': {1: (0, 0),\n            2: (4, 0),\n            3: (0, 4),\n            4: (4, 4),\n            5: (2, 2),\n            6: (6, 2)},\n \'end\': 4,\n \'highways\': [(1, 2),\n              (1, 3),\n              (1, 5),\n              (2, 4),\n              (3, 4),\n              (5, 4)],\n \'start\': 1}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您需要处理更多材料,其中序列需要再次成为“正常”列表,请使用:

\n\n
SafeConstructor.add_constructor(\n    u\'tag:yaml.org,2002:seq\',\n    SafeConstructor.construct_yaml_seq)\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

\xc2\xb9这是使用ruamel.yaml完成的,这是一个 YAML 1.2 解析器,我是该解析器的作者。如果您只需要支持 YAML 1.1 和/或由于某种原因无法升级,您应该能够对旧版 PyYAML 执行相同的操作

\n