我第一次被YAML狠狠教育,是在给一套CI流水线写配置文件的时候。当时只是漏了冒号后面的一个空格,解析器直接报了个让人摸不着头脑的错,我盯着屏幕整整十分钟才反应过来。后来接触的项目多了,从Docker Compose、Kubernetes到Ansible,再到这两年非常火的YOLOv8/YOLOv10训练配置,几乎每隔一段时间就会碰到有人栽在YAML格式的细节上。这篇东西就是想把这些年的经验一次性整理出来——什么叫YAML格式、它有哪些核心功能、每种功能对应的示例长什么样、以及实际项目中哪些坑是必须提前躲开的。不管你是刚接触配置文件的小白,还是已经被yaml文件折磨过几轮的老手,这篇文章都值得你从头到尾过一遍,尤其是后面yolov10数据配置文件的实战部分,能帮你少走很多弯路。
1. 为什么几乎所有现代工具都把配置交给了YAML
1.1 它解决的本质问题:机器要能读,人更要能读
YAML的全称是"YAML Ain't Markup Language",翻译过来就是"YAML不是标记语言"。这个命名本身就是在跟你强调:我不想变成HTML、XML那样被一堆尖括号包裹的东西,我的核心使命是让人类在不查文档的情况下,也能一眼看懂配置结构。
早期的配置文件,要么用XML这种极其啰嗦的格式,要么用INI那种层级表达能力很弱的格式。XML写一个简单的服务器地址配置,可能要写上好几行标签,维护起来头疼。JSON虽然层级清晰,但满屏的引号、花括号、逗号让人眼花缭乱,而且最致命的是——JSON不支持注释。你想在配置文件里留一句"这个参数为什么这么设",都找不到地方写。
YAML采用缩进代表层级,本质上跟Python的强制缩进是同一个思路。你不需要用花括号来标记块的开始和结束,只需要换行、缩进,结构自然就出来了。这带来的好处是:一个中等复杂度的配置,JSON可能要写三五十行,YAML往往十几行就能搞定,而且读起来像一篇结构清晰的笔记。
1.2 看它被谁用了,就知道掌握它有多重要
YAML不是某个小圈子里的冷门格式,它是目前整个基础设施领域的事实标准之一。你随手翻开一个现代工具链的文档,几乎都能看到YAML的身影:
- Docker Compose 的 docker-compose.yml
- Kubernetes 的各类资源清单
- GitHub Actions 和 GitLab CI 的流水线定义
- Ansible 的剧本和变量
- Prometheus 的告警规则
- 深度学习训练框架Ultralytics YOLO系列的数据集配置和模型结构配置
所以我的判断是:如果你需要跟任何现代软件基础设施打交道,YAML是你绕不开的一门基础课。它不是那种"学一下用一下就可以扔"的小技能,而是会反复出现在你工作里的老朋友。早一点把它的语法规则和边界情况摸透,后面在实际项目里被坑的概率就会低非常多。
1.3 需要先知道的版本真相:1.1和1.2不完全是一回事
市面上存在两个主要的YAML规范版本:YAML 1.1和YAML 1.2。很多教程会直接跳过这个话题,但它恰恰是很多"诡异行为"的源头。
YAML 1.2是2009年推出的新版规范,核心变化之一是收紧了类型转换规则。比如YAML 1.1里,yes、no、on、off这些词会被解析成布尔值true/false,但到了YAML 1.2里,它们被明确为普通字符串。问题在于,目前大量解析库(尤其是Python的PyYAML,默认支持的是YAML 1.1)仍然沿用旧规范。这就导致你用同样的文件在不同语言、不同库里解析,结果可能完全不同。我后面会专门用一节来讲这些类型转换的坑,这里你只需要建立认知:YAML的行为跟具体的解析实现强相关,不要想当然。
2. 基础语法拆解:缩进、键值对、注释和标量
2.1 缩进规则:成也缩进,败也缩进
YAML用缩进来表示层级,所以缩进必须一致且正确。这里有几条血一样的教训:
第一,必须用空格,不能用Tab。这条规则没有任何讨价还价的余地。Tab在不同编辑器、不同终端里显示的宽度不一样,同一串Tab字符在不同环境里可能被解析成4格、8格甚至更多,这会让你的文件在不同机器上呈现出完全不同的结构。几乎所有成熟的编辑器(VS Code、PyCharm等)默认情况下在写YAML时,按下Tab键实际插入的也是两个或四个空格,如果你打开某个文件发现缩进字符是Tab,建议立刻全文替换成空格。
第二,同一层级的元素,缩进量必须完全一致。比如下面这两行,device和batch_size是同一层级的键,它们的缩进就必须相同:
training: device: 0 batch_size: 16如果你写成:
training: device: 0 batch_size: 16那batch_size就会被解析为device的子节点,而不是training的子节点了。更麻烦的是,有些解析器不会立刻报错,只是默默把配置结构给改了,你程序跑起来才发现读取的参数不对,排查起来相当痛苦。
第三,推荐统一用两个空格缩进。YAML规范只要求同一层级缩进一致,并不规定具体缩进多少。但实际项目中,两个空格是事实上的标准,几乎所有官方示例、开源项目都这么写。你个人完全可以按自己习惯来,但如果你要跟别人协作,或者要参考别人的开源写法,两个空格会显得最和谐。
2.2 键值对的基本写法与常见翻车点
YAML最基础的单位是键值对:
name: yolov10 epochs: 100看起来简单,但这里最容易翻车的是冒号后面的空格。冒号后面必须有一个空格,然后才是值。如果你写成name:yolov10,很多解析器会把name:yolov10整个当成一个字符串,而不是键值对。我在前面提到CI流水线那次报错,根源就是这个。
还有一类特殊情况值得注意:如果键值对的值是一个URL,比如url: https://example.com,那么冒号后面依旧需要先有一个空格,然后整个URL不需要加引号也能被当成字符串解析。但是如果你在一个键的key本身内部要用冒号,就必须给整个key加引号,例如:
"http://example.com": 这是键本身含有冒号的情况日常项目中很少会这么干,但你要知道这个规则存在。
2.3 注释、文档分隔符和文件编码
注释用井号#表示,支持整行注释,也支持行内注释:
# 这是整行注释 name: yolov10 # 这是行内注释,井号前必须有空格注释在解析时会被完全忽略,所以你可以放心大胆地在配置里写"为什么这个参数是10"、"这里等模型训练完再改"这类提示性文字。这也是YAML比JSON更适合做配置文件的核心原因之一。
一个YAML文件可以以---作为文档开始标记,以...作为文档结束标记。这两个标记在绝大多数项目中是可选的,但有一个场景你一定会遇到:多个YAML文档拼接在同一个文件中,比如Kubernetes的部署清单里经常一个文件用---分隔多个资源。你后面解析这种文件时,需要用yaml.safe_load_all()而不是yaml.safe_load()才能把所有文档都读出来。
文件编码方面,我强烈建议统一使用UTF-8且不带BOM。BOM是文件开头的几个不可见字节,有些Windows编辑器会自动加上,有些解析器读到BOM会在第一个key的名字里混入乱码字符,导致"找不到配置项"这种异常。VS Code右下角可以直接切换编码,保存时选择UTF-8即可。
2.4 标量类型到底有哪些
"标量"是YAML术语,就是指单个的值,比如一个字符串、一个数字、一个布尔值。YAML支持的类型比你想象中丰富一点:
| 写法 | 解析结果(以PyYAML默认行为为准) | 说明 |
|---|---|---|
count: 10 | 整数int | 常规十进制整数 |
ratio: 0.85 | 浮点float | 常规小数 |
ratio2: 1.5e-3 | 浮点float | 科学计数法也被支持 |
enable: true | 布尔bool | true/false两词 |
enable2: True | 布尔bool | 大小写都能识别 |
nothing: null | 空值None | 也可以是~,或者干脆留空 |
date: 2024-01-01 | 日期date | 这是容易踩坑的重灾区 |
name: yolov10 | 字符串str | 普通无引号文本就是字符串 |
这里先打个预防针:日期字符串2024-01-01在很多解析器里会被当成date对象,而不是字符串。如果你的配置里某个字段正好长这样,但它实际应该当作字符串用(比如文件名、编号),那就得给这个值加上引号:
file_name: "2024-01-01"否则你读出来的是一个带日期属性的对象,直接拿去拼字符串路径时会得到一串意想不到的"2024-01-01 00:00:00"。
3. 序列、嵌套结构、多行文本,这些高频功能的手写示范
3.1 列表与嵌套列表
YAML的列表用短横线-开头。下面是一个简单例子:
gpus: - 0 - 1 - 2这个表示gpus这个键的值是[0, 1, 2]这个列表。注意短横线后面也要有一个空格。如果你写-0,解析器会认为这是一个以短横线开头的字符串,语义完全变了。
列表可以嵌套:
matrix: - - 1 - 2 - - 3 - 4这表示[[1,2],[3,4]]。这种写法平时不多见,但如果你在配置里做组合搜索、超参数网格,就会用上。
3.2 列表里的字典和字典里的列表
这是配置文件里最高频的结构,也是理解YAML层级的关键点。我们以YOLOv8/v10的模型结构配置场景来演示:
backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, Conv, [128, 3, 2]]这里的backbone是一个字典,它的value是一个列表,列表的每个元素又是一个列表。缩进来看:backbone在顶层缩进0格,它下面的两个-缩进2格,所以这两个-都属于backbone这个键的列表项。
这种"巢式结构"你要记住一个判断方法:看缩进量,不看符号。在YAML里,只要一个块内出现了-,那么这个块整体是一个列表;列表项本身又可以是一个字典,比如:
train: - name: coco128 epochs: 100 - name: custom epochs: 300这里train的value是一个列表,列表里每一项都是包含name和epochs两个键的字典。我们看缩进,name和epochs都缩进在同一个列表项下面,它们是这个字典的两个键。
3.3 多行文本:| 与 > 的区别真的得记清楚
这是YAML语法里最容易被忽略却又极其实用的一个功能。我先讲结论:
|表示字面风格,换行会被原样保留。适合写配置说明、日志模板、多行命令脚本片段。
>表示折叠风格,换行会被折叠成空格,但空行会保留为换行。适合写一段比较长的文本,希望它在文件里可读性好,但最终字符串里是一整行连续的句子。
举个例子:
literal_block: | 第一行 第二行 第三行 folded_block: > 这些文字 在解析之后 会变成一句话两段内容经过解析后,literal_block对应的字符串是"第一行\n第二行\n第三行\n",folded_block对应的是"这些文字 在解析之后 会变成一句话\n"。这个区别在实际项目中非常有用。比如你想在配置里放一段markdown说明,就用|;想放一段需要拼成完整文本的提示语,就用>。
多行文本还有一个进阶标记载chomping指示符:在|或>后面可以跟-表示去掉末尾换行,跟+表示保留多个末尾空行。
command: |- echo "hello" echo "world"这样解析后末尾不会带多余换行符,在很多需要精确拼接命令字符串的CI配置里很受用。
3.4 Flow Style:什么时候适合用
如果你觉得缩进写太长了,YAML还支持行内风格,称为flow style,就是把结构写在中括号[]和花括号{}里,跟JSON的数组和对象长得比较像:
model: input_size: [640, 640] metadata: {author: "tc", version: 2}我的建议是:这种风格适合写"一眼就能看懂、不太需要拆分的短结构",比如尺寸、坐标、颜色值。如果结构很复杂还全挤在一行里,那反而失去了YAML"以缩进表达结构"的核心优势。我自己写配置时,复杂的嵌套一律使用块状缩进,只有简单的固定值才用flow style。
4. 锚点、别名与合并键:一份配置复用到爽
4.1 锚点&和别名*
YAML支持定义锚点(anchor)和别名(alias),这相当于给某个value或节点起个名字,后面反复引用。很多教程把这个功能跳过,但我觉得它是YAML作为配置语言最"高级"的地方之一。
语法是:在某个值前用&名字定义锚点,后面用*名字引用。
base_data: &common path: ./datasets train: images/train val: images/val training_a: <<: *common epochs: 100 training_b: <<: *common epochs: 300这里&common把path、train、val这三个键值对整体标记为名为common的锚点。下面training_a和training_b用<<: *common把这个锚点里的所有内容展开合并进来。是不是有点像编程语言里的继承?没错,实际效果就是一份基础配置被复用到了多个子配置中。
注意锚点不一定非要搭配合并键用,它也可以直接引用一个标量:
default_epochs: &default_epochs 100 training: epochs: *default_epochs这样做的好处是:你只需要改锚点定义的那一个地方,所有引用它的地方都会自动跟着变。对于深度学习实验来说,当你需要同时维护多个训练计划、多个模型变体时,这个机制能极大减少重复劳动。
4.2 合并键<<怎么用细节很多
合并键<<是一个映射类型专用的逻辑,把右边引用的映射内容合并到当前映射中。有几个细节我认为值得单独拿出来讲:
第一,显式定义的键优先于合并进来的键。比如:
defaults: &defaults lr: 0.01 batch: 16 train_small: <<: *defaults batch: 8这里train_small的batch最终是8,而不是从defaults里继承的16。这个优先级是YAML规范的明确要求,实际用起来非常顺手:你可以在基础配置里写全套默认值,然后在某个具体实验配置里只覆盖需要改的那几个。
第二,一个映射可以合并多个锚点。写法是:
final: <<: [*base1, *base2]这时多个来源的键会被合并在一起,同样遵循"显式定义优先"的规则;如果多个合并来源之间存在同名键,后面的会覆盖前面的(不同解析器实现可能存在细节差异,建议在关键场景用脚本验证)。
第三,合并键并不是所有YAML解析器都原生支持得一样好。PyYAML对<<的支持总体不错,某些严格遵循YAML 1.2 core schema的实现可能行为不同。如果你编写的配置要跨语言、跨工具链使用,建议先在目标解析器里跑一个最小示例验证一下。
4.3 什么时候值得用锚点
锚点不是越多越好。我自己在使用时的经验是:一个配置文件中同一份片段只要出现超过一次,就值得用锚点重构。比如,多份训练参数配置如果只是学习率、epochs不同,其余backbone结构完全一样,那就应该把backbone结构定义成锚点,再在各份配置中合并引用。
但如果只是两三处简单的重复,强行抽锚点反而会让配置文件变得难读——读者需要来回跳转才能看懂完整结构。这个平衡点要把握好,我更倾向于"重复规模够大才抽"。
锚点还有一个很实用的场景:在同一个YAML文件里维护多组配置文件,它们之间存在大量共同的默认参数。比如做YOLO系列消融实验时,你需要对比几组训练设置,就可以把common部分抽成锚点,每组实验独立成段。
5. 类型系统与自动转换:最容易被YAML坑到的地方
5.1 引号不是装饰品
YAML允许字符串不带引号,也可以带单引号或双引号,几种写法效果并不完全等价。无引号字符串是YAML最常用的,但也是一个巨大的陷阱来源:因为解析器会自动做类型推测。
举一个印象深刻的例子,有人写:
version: 1.10解析出来后,这个值是浮点数1.1,而不是字符串"1.10"。如果你拿这个值去拼软件下载链接,就会得到网址里版本号变成了1.1,问题非常隐蔽。正确的写法是:
version: "1.10"只要加了对双引号,哪怕里面是"1.10"、"1.0"、"true"、"null",都一律按字符串处理,不再做类型转换。引号绝不是可有可无的装饰。
5.2 布尔陷阱:yes/no/on/off
在最常见的YAML 1.1解析规则下,这几个词会成为布尔值:
flag: yes解析出来是True,而不是字符串"yes"。到了YAML 1.2规范里,yes/on这些词又明确只能是字符串了。这种规范不一致导致了相当多的跨解析器兼容问题。你的做法应该是:任何时候想表达字符串都不应该裸写这些词,要写"yes"、"no"、"on"、"off"。要表达布尔值,就正经写true或false(全小写最安全)。
5.3 数字与版本号陷阱
除了前面提到的1.10变浮点问题,还有几个数字相关的高频坑:
- 以
0开头的数字,比如0123,在YAML 1.1里会被当作八进制数解析(十进制的83);YAML 1.2里又变了。你要表示一个带前导零的编号,直接加引号。 - 以
0x开头的是十六进制,以0o开头的是八进制,以0b开头的是二进制。这些如果出现在意外的地方,也会导致诡异的数值变化。 - 像
+12、-12这种正负号开头,解析器也能正确处理成整数。
所以我的建议是:凡是可能被误认为类型的字符串,全加引号,不给解析器自由发挥的机会。文件配置里能少一个坑就少一个坑。
5.4 空值陷阱:null与空字符串不是一回事
YAML里null、~、留空的键值都能表示空值,解析之后基本都是None。但空字符串和空值不是一回事:
a: b: ""这里a的值是None(空值),b的值是空字符串""。如果你在Python代码里判断"if cfg['a']:",两者结果都是False,但类型完全不同,一旦做字符串拼接或者写入数据库,行为就有差异。我的建议是:要表达"空字符串"就明确写"",不要依赖留空。
5.5 特殊字符陷阱:* # ! 又是什么
- 以
*开头的内容会被解析成锚点引用而不是字符串。你想写一个以星号开头的字符串,必须加引号。 #如果出现在值的开头,或者前面没有空格时,会被当作注释的一部分吗?实际上,如果#出现在值内部而不是开头,很多解析器会把#及之后的内容视为注释,所以路径里如果包含#,建议用引号包起来。!开头的内容是YAML标签语法,比如!something会被某些解析器尝试解释为类型标签。无引号裸写的形如!experiment/run-1的字符串,可能会被解释为tag,而不是你期望的字符串。
这些特殊字符用到的频率不高,但一旦踩中,报错信息往往非常难懂,排查起来也费劲。
6. 实战:从零创建yolov10能用的YAML配置文件
6.1 yolov10到底需要哪些yaml文件
这两年YOLO系列很火,其中yolov10也延续了Ultralytics体系用YAML文件管理配置的习惯。一个典型项目会用到两类yaml文件:
- 数据集配置data.yaml:描述数据集的路径、类别名、类别数量。
- 模型结构配置yolov10n.yaml / yolov10s.yaml等:描述模型结构,比如backbone、head的结构和nc类别数。
很多人第一次接触yolov10时最大的困惑就是:"我该在哪创建这些yaml文件?"其实就是用任意文本编辑器新建一个后缀为.yaml或.yml的文件,比如data.yaml、yolov10-custom.yaml,然后按格式写内容。文件后缀两种都行,Ultralytics两套都认,但建议全项目统一用一种,避免混淆。
6.2 data.yaml怎么写:路径、nc、names
一份最基础的yolov10数据集配置大概是这样的:
# 数据集配置文件 data.yaml path: ./datasets/custom_dataset train: images/train val: images/val test: images/test nc: 2 names: ['cat', 'dog']这里有几个细节:
path是数据集根目录,train、val、test是相对根目录的图片文件夹路径。yolov10在读取时会用path去拼接train得到最终实际路径,例如./datasets/custom_dataset/images/train。如果你把train写成绝对路径,也是可以的,但这样配置可移植性差,换台机器就得改。我的建议是:使用相对路径,尤其path用相对项目根目录的路径,这样你把配置文件和数据集一起挪走,改起来成本最低。
nc是类别数量,必须和names列表的长度一致。这个坑是重灾区。我自己就经历过:类别名列表定义了5个类别,忘了改nc还保留默认的2,训练直接报类别数不匹配错误。如果配置里同时存在nc,训练时会优先检查是否等于len(names),不一致会警告或报错。
names是类别名称列表,也可以用字典形式:
names: 0: cat 1: dogUltralytics两种写法都支持,但列表形式更常见,也更易读。
6.3 模型结构yaml怎么写:backbone和head
如果你要自定义模型结构,还需要写模型结构yaml。以YOLO系列的通用模板为例,它的顶层结构大致是:
# yolov10-custom.yaml nc: 2 scales: s: [0.33, 0.50, 1024] m: [0.67, 0.50, 768] backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, C2f, [128, True]] head: - [-1, 1, Conv, [64, 1, 1]] - [-1, 1, Detect, [nc]]这里的nc定义了模型输出的类别数,必须和data.yaml里的nc一致(因为整个模型推理输出矩阵的维度由它决定)。backbone和head是列表,每个列表项包含类似[输入来源, 模块重复次数, 模块类型, 模块参数]这样的信息。scales是对不同模型尺寸缩放比例的配置,一般在预训练权重匹配时会用到。
对于大多数使用者来说,不需要从零手写模型结构,而是复制一份官方已有的配置文件(比如yolov10n.yaml),然后把nc改掉。这是最稳妥的做法,因为手写容易在结构层数、模块编号之类的细节上出错,一旦出错,模型加载阶段就跑不起来。
6.4 Python端验证配置是否被正确解析
创建完yaml文件,我强烈建议先做一次解析验证,而不是直接扔进训练脚本。因为很多训练框架会在加载yaml时静默吞掉某些错误,等用到对应字段时才爆炸,那时候报错栈已经拉得很长了,定位成本高。
验证方法很简单,写个几行的Python脚本:
import yaml from pathlib import Path cfg_path = Path("data.yaml") with cfg_path.open(encoding="utf-8") as f: cfg = yaml.safe_load(f) print(cfg) print("nc =", cfg["nc"]) print("names =", cfg["names"]) print("实际训练图像路径 =", f'{cfg["path"]}/{cfg["train"]}')如果你用的是PyYAML,记得用safe_load而不是load。load在没有显式指定Loader时会触发警告,而且如果文件里含有恶意构造的标签,还可能造成严重的安全问题,所以一律safe_load。
6.5 训练时的常见报错与原因
在我实际使用yolov10等Ultralytics系列的过程中,遇到过的几个高频率报错,在这里一并列出来:
| 报错/现象 | 大概率原因 | 处理方法 |
|---|---|---|
Dataset not found或路径非法 | data.yaml里的path写错、相对路径基准不对 | 用Python打印拼接后的完整路径,确认存在 |
| 类别数不匹配 | data.yaml的nc与模型结构yaml里的nc不一致,或names长度不等于nc | 打开两个yaml文件手动对齐 |
| 键名找不到、NoneType错误 | yaml缩进错误,导致键被解析到错误层级 | 打印cfg内容检查结构 |
| 中文路径乱码 | yaml文件保存为非UTF-8编码 | 强制以UTF-8保存,并在代码里用encoding="utf-8"读取 |
| 某个字段解析成bool/float而非字符串 | 无引号写yes、on、1.0、2024-01-01之类 | 加双引号强制成字符串 |
这些报错里的绝大多数,根源都在YAML格式细节上,而不是模型本身的问题。所以一个扎实的YAML基础,在深度学习日常调试里其实是"救命"级别的技能。
7. 工具链与日常调试验收:把YAML当正经工程来管
7.1 用工具做语法校验,不要全靠肉眼
YAML的解析报错有时候很含蓄,比如"expected , but found ..."这类信息,新手看了完全不知道在哪里错。所以我建议你在编辑器层面就配上校验工具。
VS Code里安装YAML扩展,它会在你编辑时实时标出缩进错误、重复键、类型异常。命令行层面,可以用yamllint做语法和风格检查:
yamllint data.yaml它不仅能告诉你"第几行有问题",还会给出具体是什么问题。这个工具对团队协作尤其重要,因为它是形成统一配置规范的好帮手。
7.2 格式转换与格式化:yq和Python双管齐下
有一个命令叫yq,你可以把它理解为"YAML版的jq"。它能对yaml做查询、转换和格式化,也能跟JSON互相转换。比如你想快速把一段JSON转成yaml格式,可以直接:
yq -P sample.json > sample.yaml有时候,我们从网上复制的配置片段格式很乱(Tab混用、缩进不齐),手改效率太低,我一般直接用yq做一次格式化处理。如果环境里没有yq,用Python也可以:
import yaml with open("data_raw.yaml", encoding="utf-8") as f: cfg = yaml.safe_load(f) with open("data_formatted.yaml", "w", encoding="utf-8") as f: yaml.dump(cfg, f, allow_unicode=True, sort_keys=False, indent=2)提醒几个细节:allow_unicode=True保证中文不会被转义成\uXXXX;sort_keys=False保留原来的键顺序,不然格式化之后键会被按字母重新排序,跟原文件的内容顺序对不上;indent=2统一成两个空格缩进。
7.3 安全实践与团队规范
YAML的解析器有安全风险。任意对象反序列化可能导致程序执行恶意代码,这是业内非常著名的安全问题。具体原则很简单:永远不要直接加载来源不明的YAML文件,永远使用safe_load系列API。在Ultralytics这类框架内部,它们处理模型配置时用的是安全加载路径,但你自己的脚本里一定要养成习惯。
团队协作时,我建议在项目里放一份.yamllint规则文件,约定好缩进两个空格、行尾统一、禁止Tab等规则。这样不管谁来改配置文件,行为都有据可依。日常你也不用记住全部规则,编辑器扩展和yamllint会在你保存时提醒你。
7.4 最后分享一个我现在的习惯
我现在遇到任何需要写配置文件的场景,都会先写一小段最小可用版本,跑一个解析脚本验证,确认结构正确后再往上补内容。这个习惯是从几次"配置文件看起来没问题、实际读出了走样的数据"的惨痛教训里养出来的。YAML这门"语言"说到底非常简单,难的是它无处不在的类型转换和隐式规则。只要你愿意花十分钟把本文提到的这些坑过一遍,并在实际项目中多加验证,YAML就不会再成为你改配置时的拦路虎了。