如何使用 PyYAML 读取 python 元组?

新手上路,请多包涵

我有以下名为 input.yaml 的 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

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

 import yaml

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

print(data)

结果是以下数据结构:

 { '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
}

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

 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)

然而,这似乎是一个 hack。有什么方法可以指示 PyYAML 直接将它们读取为元组而不是列表?

原文由 Aadit M Shah 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.9k
2 个回答

我不会把你所做的事情称为你正在尝试做的事情。根据我的理解,您的替代方法是在 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

示例代码:

 import yaml

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

print(d)

这将输出:

 {'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)]}

原文由 idjaw 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

 import pprint
import ruamel.yaml
from ruamel.yaml.constructor import SafeConstructor

def construct_yaml_tuple(self, node):
    seq = self.construct_sequence(node)
    # only make "leaf sequences" into tuples, you can add dict
    # and other types as necessary
    if seq and isinstance(seq[0], (list, tuple)):
        return seq
    return tuple(seq)

SafeConstructor.add_constructor(
    u'tag:yaml.org,2002:seq',
    construct_yaml_tuple)

with open('input.yaml') as fp:
    data = ruamel.yaml.safe_load(fp)
pprint.pprint(data, width=24)

打印:

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

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

 SafeConstructor.add_constructor(
    u'tag:yaml.org,2002:seq',
    SafeConstructor.construct_yaml_seq)


¹这是使用 ruamel.yaml 一个 YAML 1.2 解析器完成的,我是它的作者。如果你只需要支持 YAML 1.1 和/或由于某种原因无法升级,你应该能够对旧的 PyYAML 做同样的事情

原文由 Anthon 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题