一、什么是 YAML?

YAML 是一种人类可读的数据序列化语言,常用于配置文件(如 Docker Compose、Kubernetes 配置、Ansible Playbooks 等)。其主要特点包括:

  • 使用缩进表示层级结构(类似 Python)
  • 支持标量(字符串、数字、布尔)、列表和字典
  • 支持注释
  • 支持锚点(&)和引用(*)实现数据复用

示例 YAML 文件(config.yaml):

database:
  host: localhost
  port: 5432
  username: admin
  password: secret

features:
  - user_auth
  - logging
  - caching

debug: true

二、PyYAML 简介

PyYAML 是一个用于在 Python 中解析和生成 YAML 数据的第三方库。它支持完整的 YAML 1.1 规范,并提供了简单易用的 API。

主要功能:

  • 将 YAML 字符串或文件加载为 Python 对象(如字典、列表)
  • 将 Python 对象转储为 YAML 格式
  • 支持自定义数据类型和标签
  • 提供安全加载模式(防止任意代码执行)

三、安装 PyYAML

使用 pip 安装 PyYAML:

pip install pyyaml

注意:包名是 pyyaml,但导入时使用 import yaml


四、基本用法

1. 加载 YAML 数据(解析)

使用 yaml.load() 或推荐的安全方式 yaml.safe_load()

import yaml

# 示例 YAML 字符串
yaml_str = """
name: Alice
age: 30
skills:
  - Python
  - Data Analysis
  - Machine Learning
active: true
"""

# 解析 YAML 字符串
data = yaml.safe_load(yaml_str)
print(data)
# 输出:
# {'name': 'Alice', 'age': 30, 'skills': ['Python', 'Data Analysis', 'Machine Learning'], 'active': True}

从文件加载:

with open('config.yaml', 'r', encoding='utf-8') as file:
    config = yaml.safe_load(file)
    print(config)

2. 生成 YAML 数据(转储)

使用 yaml.dump() 将 Python 对象转换为 YAML 字符串。

data = {
    'server': {
        'host': '192.168.1.1',
        'port': 8080,
        'ssl': True
    },
    'users': ['alice', 'bob', 'charlie']
}

yaml_str = yaml.dump(data, default_flow_style=False, sort_keys=False)
print(yaml_str)

输出:

server:
  host: 192.168.1.1
  port: 8080
  ssl: true
users:
- alice
- bob
- charlie

常用参数说明:

  • default_flow_style=False:使用缩进块风格而非内联风格
  • sort_keys=False:保持字典键的原始顺序(PyYAML 默认排序,关闭以保留顺序)

五、高级特性

1. 锚点与引用(Anchors & Aliases)

YAML 支持通过 & 定义锚点,* 引用已定义的内容,避免重复。

defaults: &defaults
  timeout: 30
  retries: 3

service1:
  <<: *defaults
  url: https://api.service1.com

service2:
  <<: *defaults
  url: https://api.service2.com
  timeout: 60

PyYAML 能正确解析此类结构,合并字段(<< 表示合并)。

2. 自定义 Python 对象序列化

可以注册自定义类型,实现复杂对象的 YAML 序列化。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"

# 注册序列化器
def person_representer(dumper, data):
    return dumper.represent_mapping('!person', {'name': data.name, 'age': data.age})

yaml.add_representer(Person, person_representer)

# 使用
p = Person("Bob", 25)
print(yaml.dump(p))
# 输出:
# !person {age: 25, name: Bob}

反序列化需使用 yaml.add_constructor() 注册构造器。


六、安全注意事项

⚠️ 重要警告:不要使用 yaml.load() 处理不可信的 YAML 输入!

yaml.load() 支持任意 Python 对象构造,可能执行恶意代码:

malicious_yaml = """
!!python/object/apply:os.system ['echo "PWNED"']
"""
# yaml.load(malicious_yaml)  # 危险!会执行系统命令

✅ 正确做法:始终使用 yaml.safe_load(),它仅支持标准 YAML 类型(str、int、list、dict 等),不执行任意代码。

如果需要加载自定义类型,应使用 yaml.full_load() 并确保输入可信。


七、常见问题与技巧

1. 保持字典顺序

Python 3.7+ 字典默认有序,但 PyYAML 默认会排序。可通过以下方式保留顺序:

from collections import OrderedDict

data = yaml.safe_load(yaml_str, Loader=yaml.SafeLoader)
# 或者指定加载器使用 OrderedDict

2. 处理中文字符

写入文件时注意编码:

with open('output.yaml', 'w', encoding='utf-8') as f:
    yaml.dump(data, f, allow_unicode=True, default_flow_style=False)

设置 allow_unicode=True 避免中文被转义。


八、替代方案对比

库名 特点
PyYAML 功能全面,社区广泛,支持完整 YAML 规范
ruamel.yaml 更现代,严格遵循 YAML 1.2 标准,更好保留格式和注释,适合配置文件编辑
strictyaml 强调安全性与模式验证,适合处理用户输入

对于大多数项目,PyYAML 已足够;若需更高安全性或格式保留,可考虑 ruamel.yaml


九、总结

PyYAML 是 Python 中处理 YAML 数据的事实标准库,具备以下优点:

  • API 简洁直观
  • 功能强大,支持复杂结构
  • 社区成熟,文档丰富

但在使用时务必注意:

  • 始终优先使用 yaml.safe_load()
  • 避免处理不可信的 YAML 源
  • 合理使用高级特性以增强可维护性

掌握 PyYAML,能让你轻松应对配置管理、数据交换等常见任务,是 Python 开发者的实用工具之一。


参考资料

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐