您现在的位置是:网站首页> 编程资料编程资料

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

2023-05-25 323人已围观

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

楔子

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

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

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

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

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

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

字典

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

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

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

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

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

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

Python 解析的结果如下:

import yaml config = """ info: {name: satori, address: 东方地灵殿} """ print(yaml.safe_load(config)) """ {   'info': {'name': 'satori',             'address': '东方地灵殿'} } """

还是很简单的。

数组

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

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

Python 解析的结果如下:

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

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

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

Python 解析的结果如下:

import yaml config = """ -   - 古明地觉   - 古明地恋   - 雾雨魔理沙 """ print(yaml.safe_load(config)) """ [['古明地觉', '古明地恋', '雾雨魔理沙']] """ # 更简洁的写法 config = """ - [古明地觉, 古明地恋, 雾雨魔理沙] """ print(yaml.safe_load(config)) """ [['古明地觉', '古明地恋', '雾雨魔理沙']] """

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

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

Python 解析的结果如下:

import yaml config = """ girl:   - 古明地觉   - 古明地恋   - 雾雨魔理沙 """ print(yaml.safe_load(config)) """ {'girl': ['古明地觉', '古明地恋', '雾雨魔理沙']} """ # 注意:上面的 girl 对应的是数组 # 因为每个元素前面都有 - # 但如果没有的话会发生什么? config = """ girl:     古明地觉     古明地恋     雾雨魔理沙 """ print(yaml.safe_load(config)) """ {'girl': '古明地觉 古明地恋 雾雨魔理沙'} """ # 我们看到整体相当于是一个字符串 # 类似于 html,之间用一个空格代替 # 因此如果内容比较长,我们可以写成多行 # 但是注意:每一行前面必须有空格

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

import yaml config = """ girl:     # 会对应一个数组     - 古明地觉     - 古明地恋     - 雾雨魔理沙      place1:     # 虽然不是数组,但是内部是字典的形式     # 所以会对应一个含有三个键值对的字典     古明地觉: 东方地灵殿     古明地恋: 东方地灵殿     雾雨魔理沙: 魔法森林 place2:   # 是数组,数组里面每个元素是一个字典   - 古明地觉: 东方地灵殿   - 古明地恋: 东方地灵殿   - 雾雨魔理沙: 魔法森林 """ print(yaml.safe_load(config)) """ {     'girl': ['古明地觉', '古明地恋', '雾雨魔理沙'],     'place1': {'古明地觉': '东方地灵殿',                 '古明地恋': '东方地灵殿',                 '雾雨魔理沙': '魔法森林'},     'place2': [{'古明地觉': '东方地灵殿'},                 {'古明地恋': '东方地灵殿'},                 {'雾雨魔理沙': '魔法森林'}] } """

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

标量

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

import yaml config = """ int: 123 float: 3.14 bool:     - true      - false # 波浪号表示空     NoneType: ~   datetime: 2020-11-11 12:12:13 # 使用两个 ! 可以进行类型强转 # 不过几乎用不到  cast:     - !!str 123     - !!str true   """ 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 是支持使用引号括起来的。

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

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

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

引用

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

import yaml 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'} } """

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

import yaml config = """ - &name 古明地觉  - 古明地恋 - 雾雨魔理沙 - *name """ print(yaml.safe_load(config)) """ ['古明地觉', '古明地恋',   '雾雨魔理沙', '古明地觉'] """

生成 yaml 文件

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

import yaml data = {     "girl": [         {"name": "古明地觉", "age": 17, "place": "东方地灵殿"},         {"name": "古明地恋", "age": 16, "place": "东方地灵殿"},         {"name": "雾雨魔理沙", "age": 16, "place": "魔法森林"}     ],     "other": {         "古明地觉": {"nickname": ["小五", "少女觉", "觉大人", "小五萝莉"],                  "length": 155},         "古明地恋": {"nickname": ["恋恋"], "length": 155},         "雾雨魔理沙": {"nickname": ["摸你傻"], "length": 155}     } } with open("cfg.yml", "w", encoding="utf-8") as f:     yaml.dump(data, f, allow_unicode=True, indent=2)

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

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

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

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

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

import yaml with open(".gitlab-ci.yml", "r", encoding="utf-8") as f:     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配置文件的资料请关注其它相关文章!

-六神源码网