PythonizeYAML
教程
API
GitHub
  • English
  • 简体中文
教程
API
GitHub
  • English
  • 简体中文
  • 教程

    • 教程
    • 加载与导出
    • 编辑值
    • 命令行
    • 注释、样式、标签与锚点
    • 加载不受信任的 YAML
  • 实战示例

    • 编辑服务配置
    • 构建发布元数据命令

加载与导出

本章介绍库的核心循环:把 YAML 读进可编辑的 Python 对象、再写回去,以及保证"没被修改的地方输出与输入完全一致"的往返保证。

1. 安装

python -m pip install pythonizeyaml

示例统一使用与 PyYAML 一致的导入方式:

import pythonizeyaml as yaml

2. 加载 YAML

yaml.load() 读取一个文档:

document = yaml.load(
    """
# Settings for the local web service.
service:
  host: 127.0.0.1
  port: 8080
"""
)

返回值是一个 Document。映射与序列根对应 dict 或 list 的子类(DocumentMapping、DocumentSequence),所以你对 Python 容器的全部经验都适用:

document["service"]["port"]     # 8080
len(document)                   # 1
"service" in document           # True

与 PyYAML 的差别在日常使用中看不出来,在底层却至关重要:这个对象记得每个值来自哪里——它的注释、引号、缩进——并且在你编辑时一直保留这份记忆。

load() 也接受 UTF-8 bytes 和任何带 .read() 方法的对象,因此文件句柄和网络响应无需预处理即可使用:

with open("config.yaml", encoding="utf-8") as handle:
    document = yaml.load(handle)

3. 导出 YAML

yaml.dump() 是镜像操作。不传流时返回 YAML 文本;传入可写流则写入并返回 None:

text = yaml.dump(document)

with open("out.yaml", "w", encoding="utf-8") as handle:
    yaml.dump(document, handle)

导出选项都是仅限关键字参数,并由所有导出函数共享:

yaml.dump(data, indent=4)             # 嵌套缩进宽度,连字符与父键平齐
yaml.dump(data, width=100)            # 首选最大行宽
yaml.dump(data, explicit_start=True)  # 输出起始 ---
yaml.dump(data, explicit_end=True)    # 输出结尾 ...

一些 PyYAML 关键字——allow_unicode、default_flow_style、sort_keys、encoding——会被接受但忽略:它们与逐字节保留相冲突,而那正是本库的核心目标。Dumper 占据 PyYAML 的第三个位置参数槽,同样被忽略。其他任何关键字都会抛出 TypeError。

4. 往返保证

加载一个未经修改的文档再导出,结果与源文本完全一致:

source = """\
# Published by release.py.
version: '0.3.0'
artifacts:
  - pythonizeyaml
  - pythonizeyaml-docs
"""

assert yaml.dump(yaml.load(source)) == source

0.3.0 外面的单引号、块序列的连字符、注释和空行全部保留。修改一个值时,只有该值的字节会被重写——而且样式会保留,带引号的标量仍然带引号:

document = yaml.load(source)
document["version"] = "0.4.0"

assert yaml.dump(document) == source.replace("'0.3.0'", "'0.4.0'")

5. 标量解析

默认情况下,标量按 PyYAML 的 YAML 1.1 语义解析,因此经典的 YAML 1.1 写法都与 PyYAML 用户所期望的一致:

yaml.safe_load("verbose: yes\n")    # {'verbose': True}
yaml.safe_load("mode: off\n")       # {'mode': False}
yaml.safe_load("mask: 010\n")       # {'mask': 8}      (YAML 1.1 八进制)
yaml.safe_load("elapsed: 1:30\n")   # {'elapsed': 90}  (六十进制)
yaml.safe_load("empty: ~\n")        # {'empty': None}

当文档确实以 %YAML 1.2 指令开头时,会改用 YAML 1.2 core schema:yes 保持为字符串,010 是十进制整数 10,1:30 是字符串:

yaml.safe_load("%YAML 1.2\n---\nverbose: yes\nmask: 010\n")
# {'verbose': 'yes', 'mask': 10}

两个引擎和所有加载函数都遵循同样的规则。

6. 多行文档

多行 YAML 得到完整支持,下面的每种结构都能逐字节往返。纯标量可以跨行折叠:

yaml.load("description: a long\n  plain scalar that folds\n  into one line\n")["description"]
# 'a long plain scalar that folds into one line'

流式集合、带引号的标量、紧凑嵌套序列与显式键都可以跨行:

yaml.load("items: [\n  a,\n  b,\n  c\n]\n")["items"]
# ['a', 'b', 'c']

yaml.load('msg: "hello\n  world"\n')["msg"]
# 'hello world'

yaml.load("matrix:\n  - - a\n    - b\n  - - c\n")["matrix"]
# [['a', 'b'], ['c']]

yaml.load("? key\n: value\nsimple: x\n")
# {'key': 'value', 'simple': 'x'}

7. 多文档流

一个文件可以包含多个以 --- 分隔的 YAML 文档。load_all() 把它们作为惰性生成器逐个返回 Document 对象——流在首次迭代时才被读取和解析,且生成器只能消费一次——dump_all() 把它们写回流:

documents = list(yaml.load_all(Path("environments.yaml").read_text(encoding="utf-8")))
for document in documents:
    document["service"]["port"] += 1

text = yaml.dump_all(documents)

如果希望把整个流当作一个对象——可以整体添加、插入或删除文档——请使用 yaml.load_documents(),它返回 DocumentStream;参见API 参考。

8. 选择引擎

模块级函数共享进程级默认引擎。当你需要隔离的配置——不同的缩进,或一个可以重新配置而不影响全局状态的解析器——请实例化引擎类:

engine = yaml.YAML(yaml.IndentConfig(mapping=4, sequence=4))
text = engine.dump({"service": {"port": 8080}})

yaml.YAML 做往返并返回文档;yaml.SafeYAML 具有相同的方法,加载普通 Python 值并拒绝非标准标签。后续章节对文档做就地编辑;安全一章讲解何时应选择安全引擎。

最近更新: 2026/10/3 15:52
Contributors: originalFactor
Prev
教程
Next
编辑值