Python实现解析yaml配置文件的示例详解

2022-09-18 09:11:05

目录楔子字典数组标量引用生成yaml文件楔子前面我们介绍了ini格式的配置文件,本次来看看yaml,它的表达能力相比ini更加的强大。yaml文件以.yml结尾,在介绍它的语法结构...

目录
楔子
字典
数组
标量
引用
生成 yaml 文件

楔子

前面我们介绍了 ini 格式的配置文件,本次来看看 yaml,它的表达能力相比 ini 更加的强大。yaml 文件以 .yml 结尾,在介绍它的语法结构之前我们先来看看 yaml 的一些基本规则。

大小写敏感;
使用缩进表示层级关系,并且缩进只能用空格、不可以使用 tab 键。缩进的空格数目不重要,只要相同层级的元素左侧对齐即可;
# 表示注释,# 到行尾的所有字符都会被忽略;

yaml 支持的数据结构有以下三种:

字典:键值对的集合;
数组:多个元素组成的集合;
标量:单个、不可分割的值;

python 解析 yaml 则是通过一个名为 pyyaml 的库,直接 pip install pyyaml 即可。

下面我们来介绍一下 yaml 的数据结构。

字典

类似于 Python 的字典,使用键值对表示:

name:satori
#或者写成下面的形式
{name:satori}

Python 解析之后会是什么结果呢?

importyaml

config="""
name:satori
"""

# yaml.safe_load:只解析自己信任的输入
# yaml.unsafe_load:不检测输入的安全性
print(yaml.safe_load(config))
"""
{'name':'satori'}
"""

config="""
{name:satori}
"""
print(yamljs.safe_load(config))
"""
{'name':'satori'}
"""

在 yaml 里面,字典的 value 也可以是一个字典:

info:{name:satori,address:东方地灵殿}

Python 解析的结果如下:

importyaml

config="""
info:{name:satori,address:东方地灵殿}
"""

print(yaml.safe_load(config))
"""
{
'info':{'name':'swww.cppcns.comatori',
'address':'东方地灵殿'}
}
"""

还是很简单的。

数组

一组连字符开头的行,构成一个数组。

-古明地觉
-古明地恋
-雾雨魔理沙
#-后面要有空格
#或者写成下面的形式
[古明地觉,古明地恋,雾雨魔理沙]

Python 解析的结果如下:

importyaml

config="""
-古明地觉
-古明地恋
-雾雨魔理沙
"""

print(yaml.safe_load(config))
"""
['古明地觉','古明地恋','雾雨魔理沙']
"""

config="""
[古明地觉,古明地恋,雾雨魔理沙]
"""

print(yaml.safe_load(config))
"""
['古明地觉','古明地恋','雾雨魔理沙']
"""

并且数组的子成员也可以是一个数组:

-
 - 古明地觉
 - 古明地恋
 - 雾雨魔理沙

Python 解析的结果如下:

importyaml

config="""
-
-古明地觉
-古明地恋
-雾雨魔理沙
"""

print(yaml.safe_load(config))
"""
[['古明地觉','古明地恋','雾雨魔理沙']]
"""

#更简洁的写法
config="""
-[古明地觉,古明地恋,雾雨魔理沙]
"""

print(yaml.safe_load(config))
"""
[['古明地觉','古明地恋','雾雨魔理沙']]
"""

显然数组也可以放在字典中:

#缩进对应的空格数没有要求,但是必须一样
#对于当前这个键值对而言也可以没有缩进
girl:
-古明地觉
-古明地恋
-雾雨魔理沙
#或者下面这种形式
girl:[古明地觉,古明地恋,雾雨魔理沙]
#或者下面这种形式
{girl:[古明地觉,古明地恋,雾雨魔理沙]}

Python 解析的结果如下:

importyaml

config="""
girl:
-古明地觉
-古明地恋
-雾雨魔理沙
"""

print(yaml.safe_load(config))
"""
{'girl':['古明地觉','古明地恋','雾雨魔理沙']}
"""

#注意:上面的 girl 对应的是数组
#因为每个元素前面都有-
#但如果没有的话会发生什么?
config="""
girl:
古明地觉
古明地恋
雾雨魔理沙
"""

print(yaml.safe_load(config))
"""
{'girl':'古明地觉古明地恋雾雨魔理沙'}
"""
#我们看到整体相当于是一个字符串
#类似于html,之间用一个空格代替
#因此如果内容比较长,我们可以写成多行
#但是注意:每一行前面必须有空格

然后是一个稍微复杂的例子:

importyaml

config="""
girl:
#会对应一个数组
-古明地觉
-古明地恋
-雾雨魔理沙

place1:
#虽然不是数组,但是内部是字典的形式
#所以会对应一个含有三个键值对的字典
古明地觉:东方地灵殿
古明地恋:东方地灵殿
雾雨魔理沙:魔法森林

place2:
#是数组,数组里面每个元素是一个字典
-古明地觉:东方地灵殿
-古明地恋:东方地灵殿
-雾雨魔理沙:魔法森林
"""

print(yaml.safe_load(config))
"""
{
'girl':['古明地觉','古明地恋','雾雨魔理沙'],
'place1':{'古明地觉':'东方地灵殿',
'古明地恋':'东方地灵殿',
'雾雨魔理沙':'魔法森林'},
'place2':[{'古明地觉':'东方地灵殿'},
{'古明地恋':'东方地灵殿'},
{'雾雨魔理沙android':'魔法森林'}]
}
"""

place1 对应的是一个字典,place2 对应的是一个数组。

标量

标量属于最基本的、不可再分的值,比较简单,我们就全部都说了吧。

importyaml

config="""
int:123
float:3.14
bool:
-true
-false
#波浪号表示空
NoneType:~
datetime:2020-11-1112:12:13

#使用两个!可以进行类型强转
#不过几乎用不到
cast:
-!!str123
-!!strtrue
"""

print(yaml.safe_load(config))
"""
{
'int':123,'float':3.14,
'bool':[True,False],'NoneType':None,
'datetime':datetime.datetime(2020,11,11,12,12,13),
'cast':['123','true']
}
"""

这里可能有人已经发现了,就是字符串不需要加引号,但如果里面有特殊字符怎么办?所以 yaml 是支持使用引号括起来的。

importyaml

config="""
name1:古明地觉ax$#!!
name2:"古明地觉ax$#!!"
name3:'古明地觉ax$#!!'
"""

print(yaml.safe_load(config))
"""
{'name1':'古明地觉ax$',
'name2':'古明地觉ax$#!!',
'name3':'古明地觉ax$#!!'}
"""

对于 yaml 而言,字符串默认是从第一个不是空格的字符、匹配到最后一个不是空格的字符(如果遇到 # 直接停止)。因此如果 value 的前面或后面有空格的话,那么这些空格是不会显示的,或者当中有 #,那么 # 后面的内容也不会显示。

解决办法是使用单引号或双引号括起来,如果内部还有引号,那么需要输入两遍进行转义(如果内部的引号和外面括起来的引号相同的话)。

引用

对于 yaml 而言,还支持我们采用 & 和 * 进行引用,举个例子:

importyaml

config="""
#多了一个&db_info_ref
#相当于起了个名字,叫db_info_ref
db_info:&db_info_ref
host:127.0.0.1
port:5432
user:postgres
password:123456

deploy:
os:linux
#将内容直接扔到里面来
<;<:*db_info_ref
"""

print(yaml.safe_load(config))
"""
{
'db_info':{'host':'127.0.0.1',
'port':5432,
'user':'postgres',
'password':123456},
'deploy':{'host':'127.0.0.1',
'port':5432,
'user':'postgres',
'password':123456,
'os':'Linux'}
}
"""

& 用来建立锚点,<< 表示合并当前数据,* 表示用来引用锚点。还可以作用在数组中:

importyaml

config="""
-&name古明地觉
-古明地恋
-雾雨魔理沙
-*name
"""

print(yaml.safe_load(config))
"""
['古明地觉','古明地恋',
'雾雨魔理沙','古明地觉']
"""

生成 yaml 文件

既然能够读取 yaml 文件,那么自然也能生成 yaml 文件。

importyaml

data={
"girl":[
{"name":"古明地觉","age":17,"place":"东方地灵殿"},
{"name":"古明地恋","age":16,"place":"东方地灵殿"},
{"name":"雾雨魔理沙","age":16,"place":"魔法森林"}
],
"other":{
"古明地觉":{"nickname":["小五","少女觉","觉大人","小五萝莉"],
"length":155},
"古明地恋":{"nickname":["恋恋"],"length":155},
"雾雨魔理沙":{"nickname":["摸你傻"],"length":155}
}
}

withopen("cfg.yml","w",encoding="utf-8")asf:
yaml.dump(data,f,allow_unicode=True,indent=2)

然后我们看看生成的 yml 文件长什么样子。

Python实现解析yaml配置文件的示例详解

我们来看 yml 文件,然后反推出相应的数据结构。首先整体是一个字典,里面有 girl 和 other 两个 key。其中 girl 对应一个数组,数组里面每个元素都是字典,这是符合预期的。

然后 other 对应一个字典,而且这个字典内部有三个键值对,key 分别是:古明地觉、古明地恋、雾雨魔理沙,各自对应的 value 又是一个字典(内部有 length、nickname 两个 key,length 对应整型、nickname 对应列表)。

最后再看一个本人之前项目中的 yml 文件,可以猜猜看解析出来长什么样子。

Python实现解析yaml配置文件的示例详解

解析一下看看和你想的是不是一样的。

importyaml

withopen(".gitlab-ci.yml","r",encoding="utf-8")asf:
data=f.read()

data=yaml.safe_load(data)
print(data)
"""
{
'stages':['test'],
'cache':{'key':'${CI_COMMIT_REF_SLUG}',
'paths':['.cache/pip']},
'variables':{'PIP_CACHE_DIR':'$CI_PROJECT_DIR/.cache/pip'},
'test':{'stage':'test',
'image':'xxxxxxx/python:3.8.1-thanosclient-buster',
'only':['branches','tags'],
'services':['mysql:5.7'],
'variables':{'PROJECT':'XXXXXX',
'PIP_CACHE_DIR':'$CI_PROJECT_DIR/.cache/pip',
'MARKETING_CONFIG':'config/room/ci.cn-gz.toml',
'MYSQL_DATABASE':'activity',
'MYSQL_ROOT_PASSWORD':'password',
'MYSQL_INITDB_SKIP_TZINFO':'1'}
}
}
"""

结果应该不难想,毕竟 yaml 文件不是很复杂。

以上就是Python实现解析yaml配置文件的示例详解的详细内容,更多关于Python解析yaml配置文件的资料请关注我们其它相关文章!