必须使用 yaml(typ='rt') 或 yaml(typ='round_trip') 才能保留注释,yaml() 默认 typ='safe' 不支持;加载后注释存于节点的 ca 属性,需用同一实例 dump 才能正确输出。

ruamel.yaml 加载时注释消失?默认不保留
默认用 ruamel.yaml.YAML() 实例加载 YAML,即使文件里有行内注释或块注释,解析后 dict 或 CommentedMap 里也看不到——因为底层没启用注释保留模式。
必须显式设置 preserve_quotes=True 和 default_flow_style=False 还不够,核心是:要使用 YAML(typ='rt') 或 YAML(typ='round_trip')。
常见错误现象:yaml.load(f, Loader=yaml.RoundTripLoader) 这种旧写法在新版本已弃用,直接报 AttributeError: module 'ruamel.yaml' has no attribute 'RoundTripLoader'。
-
YAML(typ='rt')是最简且推荐的写法,“rt” 即 round-trip - 别用
YAML()(默认typ='safe'),它压根不支持注释保留 - 如果后续要 dump 回带注释的 YAML,必须用同一个
YAML实例 load 和 dump
怎么安全读取并保留所有注释?
关键不是“读取”,而是“让节点记住注释位置”。ruamel.yaml 把注释存在节点对象的隐藏属性里(比如 ca 属性),只有 round-trip 模式下的节点类型(如 CommentedMap、CommentedSeq)才带这些能力。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 始终用
yml = YAML(typ='rt')创建实例,不要复用safe或base类型 - 用
yml.load(f),不是yaml.load(f, ...)—— 后者调的是 PyYAML,和 ruamel.yaml 的注释机制无关 - 加载后检查注释是否在:打印
data.ca.items(对 map)或data.ca.comment(对节点),非空说明注释已捕获
示例:
yml = YAML(typ='rt')
with open('config.yaml') as f:
data = yml.load(f)
# 此时 data 是 CommentedMap,注释信息藏在 data.ca 中dump 时注释错位或丢失?顺序和节点类型是关键
注释不会自动“粘”在某个 key 上——它绑定在具体节点对象上。如果你手动构造 dict 再 dump,或者用 dict(data) 转成原生字典,注释就彻底丢了。
常见错误场景:
- 把
data['host'] = 'new.example.com'后直接yml.dump(data, f)—— 只要没动注释相关属性,通常还能保留;但若中间做了json.loads(json.dumps(data))这类操作,节点全换掉了 - 用
data.get('ports', []).append(8080),结果新增项没注释,原有注释还在老位置,视觉上“错位” - dump 到 StringIO 或 bytes 流时,忘了设
yml.default_flow_style = False,导致格式崩坏,注释挤到行尾甚至被截断
性能提示:round-trip 模式比 safe 慢约 2–3 倍,但只要不是高频解析千行 YAML,感知不明显。
注释内容含中文或特殊字符,编码出问题?
不是 ruamel.yaml 的锅,是 Python 文件打开方式不对。默认 open(...) 在 Windows 下可能用 cp1252,Linux/macOS 默认 UTF-8,但若文件声明了 # -*- coding: utf-8 -*- 或 BOM,行为又不同。
稳妥做法:
- 统一用
open('f.yaml', encoding='utf-8')显式指定编码 - 避免依赖系统 locale;BOM 文件可被正确识别,但不建议依赖它
- 如果 dump 出来注释变成
\u4f60\u597d,说明你用了json.dumps()中转过,而不是走yml.dump()
容易被忽略的一点:注释里的缩进空格数,会被严格保留。如果原始 YAML 用 2 空格缩进写注释,dump 出来不会自动转成 4 空格——这既是优点也是坑,改格式前先确认是否真要动注释位置。










