使用 ruamel.yaml 加载 YAML 时复制别名以分隔 Python 对象

Copy aliases to separate Python objects when loading YAML with ruamel.yaml

使用 ruamel.yaml 加载 YAML 数据时,YAML 文件中的锚点及其别名是 Python 中的同一对象:

from ruamel.yaml import YAML

yaml_str = """\
first: &reference [1, 2, 3]
second: *reference
"""

yaml = YAML()
data = yaml.load(yaml_str)

assert(data['first'] is data['second'])
# passes

data['first'].append(4)
print(data['second'])
# output: [1, 2, 3, 4]

我意识到这是一个有意的功能。但是,有没有办法告诉 load 在找到别名时改为 copy 别名?我尝试覆盖 yaml.representer.ignore_aliases,如 中所述,但这仅用于写入 YAML,而不是从中读取。

没有内置功能可以满足您的需求。任何时候遇到 一个别名,你将不得不在 composer 中递归地创建一个节点结构,而不是仅仅返回别名的锚节点。

YAML 文档中需要锚点和别名是因为需要能够 表示递归数据结构:

a = [1, 2]
a.append(a)

上述结构无法使用您 link 的答案中显示的技术转储,也无法在不扩展的情况下表示为 YAML 文档:

&id001
- 1
- 2
- *id001

使用第一段中建议的技术在加载过程中展开。

您的(非递归)示例可以使用 linked 答案中的技术转储, 并且还可以边加载边展开

最简单的解决方案,除非您想深入了解 ruamel.yaml,就是 加载您的 YAML 文档,扩展转储,然后加载转储的结果:

import sys
import ruamel.yaml

yaml_str = """\
first: &reference [1, 2, 3]
second: *reference
"""

yaml = ruamel.yaml.YAML()
yaml.representer.ignore_aliases = lambda *data: True
data = yaml.load(yaml_str)
buf = ruamel.yaml.compat.BytesIO()
out = yaml.dump(data, buf)
data = yaml.load(buf.getvalue())


assert(data['first'] is not data['second'])
assert(data['first'] == data['second'])

data['first'].append(4)
assert len(data['first']) == 4
assert len(data['second']) == 3

当然,当你有一个大文件时,这不是很有效。