首页 > 编程笔记

Python读取yaml配置文件(PyYAML模块)

在开发过程中,我们常常会用到一些固定参数或常量。对于这些常用的部分,我们往往会将其写到一个固定文件中,避免在不同的模块代码中重复出现,以保持核心代码整洁。这个固定文件我们可以直接写成一个 .py 文件,如 settings.py 或 config.py,这样的好处是能够在同一工程下通过 import 来导入当中的部分参数或变量。

但如果我们需要在其他非 Python 的平台进行配置文件共享时,写成单个 .py 文件就不是一个很好的选择。这时我们就应该选择通用的配置文件格式类型存储这些固定的参数或变量。

目前常用且流行的配置文件格式类型主要有 ini、JSON、yaml 等,如表1所示,我们可以通过标准库或第三方库来解析这些格式类型的配置文件。

表1:配置文件格式类型
配置文件格式 特点
ini ini 即 Initialize(初始化),早期是在 Windows 上配置的文件的存储格式。

ini 文件的写法通俗易懂,比较简单,通常由节(section)、键(key)和值(value)组成。Python 内置的 configparser 标准库,可以用来对 ini 文件进行解析。
JSON JSON 格式可以说是我们常见的一种文件形式,也是目前在互联网较为流行的一种数据交换格式。除此之外,JSON 有时也是配置文件的一种。

Python 内置了 JSON 标准库,可以通过 load() 方法和 loads() 方法来导入文件式和字符串式的 JSON 内容。
yaml yaml 格式(或 yml 格式)是目前较为流行的一种配置文件,典型的就是 Docker 容器里的 docker-compose.yml 配置文件。

yaml 是一种直观的能够被计算机识别的数据序列化格式,可读性好,并且容易和脚本语言交互。

现有的大部分主流编程语言都支持 yaml,如 Ruby、Java、Perl、Python、PHP、OCaml、JavaScript 等。yaml 格式清晰、简洁,跟 Python 非常适配,我们在搭建自动化测试框架的时候,可以采用 yaml 作为配置文件格式

yaml 基础

yaml 的基本语法规则如下:
yaml 支持的数据结构主要有如下3种。
对象键值对使用冒号结构表示为key: value,冒号后面要加一个空格。我们可以使用 key:{child-key1: value1, child-key2: value2, ...},可以使用缩进表示层级关系。

数组由一组连词线开头的行构成。数组前加-符号,符号与值之间需用空格分隔:
key:
    child-key1:  value1
    child-key2:  value2
-开头的行构成一个数组:
- A
- B
- C
yaml 支持多维数组,可以使用行内表示:
key: [value1, value2, ...]
一个相对复杂的示例如下:
companies:
    -
        id: 1
        name: company1
        price: 200W
    -
        id: 2
        name: company2
        price: 500W
其中 companies 属性是一个数组,每一个数组元素由 id、name、price 这 3 个属性构成。

另外,数组和对象可以构成复合结构,示例如下:
languages:
  - Ruby
  - Perl
  - Python
websites:
  YAML: yaml.org
  Ruby: ruby-lang.org
  Python: python.org
  Perl: use.perl.org
转换为 JSON 示例如下:
{
  languages: [ 'Ruby', 'Perl', 'Python'],
  websites: {
    YAML: 'yaml.org',
    Ruby: 'ruby-lang.org',
    Python: 'python.org',
    Perl: 'use.perl.org'
  }
}
纯量是最基本的、不可再分的值,使用纯量示例如下:
# 输出一个字典,其中 value 包括所有基本类型
str: "Hello World!"
int: 110
float: 3.141
boolean: true  # or false
None: null  # 也可以用~号来表示null
time: 2016-09-22t11:43:30.20+08:00 
date: 2016-09-22 
该示例实际输出结果如下:

{'str': 'Hello World!', 'int': 110, 'float': 3.141, 'boolean': True, 'None': None, 'time':datetime.datetime(2016, 9, 22, 3, 43, 30, 200000), 'date': datetime.date(2016, 9, 22)}

注意

如果字符串没有空格或特殊字符,则不需要加引号。

这里要注意单引号和双引号的区别,在 Python 中,单引号中的特殊字符会被转义,即原样输出字符,而双引号中的特殊字符不会被转义,即输出特殊字符应有的作用,例如str1: 'Hello\nWorld'str2: "Hello\nWorld",单引号中的\n原样输出,双引号中的\n输出回车。

在刚了解或刚开始使用时,我们可能对 yaml 格式掌握不熟练,容易出现格式错误,可以利用 yaml 格式校验的在线网站,校验我们写的 yaml 文件格式是否正确。

PyYAML库

在 Python 中读取 yaml 配置文件,需要用到第三方库 PyYAML。安装 PyYAML 库的命令为:
pip install pyYAML
下面针对 PyYAML 库,简要讲解读写操作。

1) 创建一个 yaml 文件 config.yml,内容如下:
name: Tom Smith
age: 37
spouse:
    name: Jane Smith
    age: 25
children:
  - name: Jimmy Smith
    age: 15
  - name1: Jenny Smith
    age1: 12

2) 利用 safe_load 方法返回一个对象:
import yaml
# 通过open方式读取文件数据
file = open('config.yml', 'r', encoding="utf-8")
# 再通过safe_load函数将数据转化为列表或字典
data = yaml.safe_load(file)
print(data)
输出结果如下:

{'name': 'Tom Smith', 'age': 37, 'spouse': {'name': 'Jane Smith', 'age': 25}, 'children': [{'name': 'Jimmy Smith', 'age': 15}, {'name1': 'Jenny Smith', 'age1': 12}]}


3) 利用 load_all 方法生成一个迭代器。如果 string 或文件包含几块 yaml 文档,我们可以使用 yaml.load_all 来解析全部的文档。
import yaml
# 通过open方法读取文件数据
file = open('config.yml', 'r', encoding="utf-8")
y = yaml.load_all(file, Loader=yaml.FullLoader)
for data in y:
    print(data)
输出结果如下:

{'name': 'James', 'age': 20}
{'name': 'Lily', 'age': 19}


4) 利用 yaml.dump 方法将一个 Python 对象转换为 yaml 文档:
import yaml
aproject = {
            'name': 'Silenthand Olleander',
            'race': 'Human',
            'traits': ['ONE_HAND', 'ONE_EYE']
           }
print(yaml.dump(aproject))
输出结果如下:

name: Silenthand Olleander
race: Human
traits:
- ONE_HAND
- ONE_EYE


若 yaml.dump 方法的第二个参数不是空值,那么第二个参数一定要是一个打开的文本文件或二进制文件,yaml.dump 方法会把生成的 yaml 文档写入文件,示例如下:
import yaml
aproject = {
            'name': 'Silenthand Olleander',
            'race': 'Human',
            'traits': ['ONE_HAND', 'ONE_EYE']
           }
f = open(r'config.yml','w')
print(yaml.dump(aproject,f))

5) 利用 yaml.dump_all 方法将多个段输出到一个文件中:
import yaml
obj1 = {"name": "James", "age": 20}
obj2 = ["Lily", 19]
with open(r'config.yml', 'w') as f:
    yaml.dump_all([obj1, obj2], f)
输出到文件 config.yml,该文件里的内容如下:

age: 20
name: James
---
- Lily
- 19

封装示例

我们可以将 yaml 操作封装成一个公共类,这样在后续配置文件引用中直接调用,完整的封装代码示例如下所示,主要是封装了 yaml 配置文件的读取和写入。
# -*- coding: utf-8 -*-
# @Time : 2023/7/26 10:29 上午
# @Project : yamlDemo
# @File : yamlUtil.py
# @Version: Python3.9.8
 
import yaml
class YamlHandler( ):
    def __init__(self,file):
        self.file = file
    def read_yaml(self,encoding='utf-8'):
        """读取yaml数据"""
        with open(self.file, encoding=encoding) as f:
            return yaml.load(f.read(), Loader=yaml.FullLoader)       
    def write_yaml(self, data, encoding='utf-8'):
        """向yaml文件写入数据"""
        with open(self.file, encoding=encoding, mode='w') as f:
            return yaml.dump(data, stream=f, allow_unicode=True)

if __name__ == '__main__':
    data = {
        "user":{
            "username": "vivi",
            "password": "123456"
        }
    }
    # 读取config.yaml配置文件数据
    read_data = YamlHandler('config.yaml').read_yaml()
    # 将data数据写入config1.yaml配置文件
    write_data = YamlHandler('config1.yaml').write_yaml(data)

推荐阅读