在 Python 中使用 PyYAML 操作 YAML 文件时,通常会遇到一个常见的问题:YAML 文件被读取后加载为一个字典,导致无法保持其原有的顺序。如果我们希望在更新 YAML 文件的内容时不改变文件的原始结构和顺序,可以通过使用 OrderedDict
和自定义加载、导出方法来实现。
用 Python 有序加载和保存 YAML 文件
以下是一个完整的代码示例,展示如何在 Python 中保持 YAML 文件顺序并实现内容的有序更新:
from collections import OrderedDict
import yaml
def ordered_yaml_load(yaml_path, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
"""
加载 YAML 文件并保持键的顺序。
:param yaml_path: YAML 文件路径
:param Loader: 使用的 YAML 加载器,默认为 yaml.Loader
:param object_pairs_hook: 默认使用 OrderedDict 来保持顺序
:return: 有序字典类型的 YAML 文件内容
"""
class OrderedLoader(Loader):
pass
def construct_mapping(loader, node):
loader.flatten_mapping(node)
return object_pairs_hook(loader.construct_pairs(node))
OrderedLoader.add_constructor(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
construct_mapping
)
with open(yaml_path, 'r') as stream:
return yaml.load(stream, OrderedLoader)
def ordered_yaml_dump(data, stream=None, Dumper=yaml.SafeDumper, **kwds):
"""
将有序字典数据保存为 YAML 文件,同时保持键的顺序。
:param data: 要保存的有序字典
:param stream: 输出流,默认为 None
:param Dumper: 使用的 YAML Dumper,默认为 yaml.SafeDumper
:param kwds: 其他 YAML 导出选项
:return: YAML 格式的字符串或写入文件
"""
class OrderedDumper(Dumper):
pass
def _dict_representer(dumper, data):
return dumper.represent_mapping(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
data.items()
)
OrderedDumper.add_representer(OrderedDict, _dict_representer)
return yaml.dump(data, stream, OrderedDumper, **kwds)
# 加载原始 YAML 文件
yaml_file_path = 'your_yaml_file.yml'
yaml_dict = ordered_yaml_load(yaml_file_path)
# 更新 YAML 文件内容
new_yaml_dict = yaml_dict.copy()
new_yaml_dict['new_key'] = 'new_value'
# 将更新后的内容有序写入 YAML 文件
with open(yaml_file_path, 'w') as f:
ordered_yaml_dump(new_yaml_dict, f, default_flow_style=False)
深入解读及操作步骤
YAML 文件顺序问题:标准字典(
dict
)在 Python 3.7 之前并不保证插入顺序,而在 YAML 文件中顺序可能很重要。因此,通过OrderedDict
来确保加载和写回时键的顺序与原文件一致。加载和导出器的自定义:通过
OrderedLoader
和OrderedDumper
类,将 YAML 的键值对映射为OrderedDict
对象,这样可以确保在加载和保存文件时保留原始顺序。实用场景:这在配置文件管理或 DevOps 操作中非常有用,特别是当配置文件的顺序对工具或系统行为有影响时。
FAQ
1. 为什么 YAML 顺序重要? YAML 文件的顺序对于某些配置文件至关重要,保持顺序有助于避免在保存文件时引发不必要的差异或冲突。
2. 是否可以将自定义类扩展用于更复杂的 YAML 文件?
是的,OrderedLoader
和 OrderedDumper
类是高度可扩展的,可以处理嵌套的 YAML 结构,同时保持嵌套键的顺序。
3. 是否有其他库可以保持 YAML 顺序?
虽然 PyYAML 是最常用的库,但 ruamel.yaml
库在处理有序 YAML 文件时可能更加方便,因为它原生支持保留顺序。