PyYAML 是一个用于解析和生成 YAML 格式的 Python 库。
PyYAML 相比 json,不仅能够解析数据,也能解析 Python 对象(非 YAML 标准语法)。
本文基于 PyYAML 3.13 版本。
QuickStart
安装纯 Python 实现版本
PyYAML 还有一个依赖 LibYAML 的版本,处理起来比纯 Python 版本要快,可以参考文档安装。
解析 yaml:
1
2
3
4
5
6
7
8
9
10
| import yaml
document = """
a: 1
b:
c: 3
d: 4
"""
print(yaml.load(document))
# {'a': 1, 'b': {'c': 3, 'd': 4}}
|
FAQ
没有嵌套集合的字典经过 dump 后格式错误?
如下的实例,字典中 b 字典的元素没有嵌套集合。
1
2
3
4
| import yaml
d = {'a': 1, 'b': {'c': 3, 'd': 4}}
print(yaml.dump(d))
|
结果为:
而不是我们期望的:
1
2
3
4
| a: 1
b:
c: 3
d: 4
|
按照官方文档的说法,这是正常的:
By default, PyYAML chooses the style of a collection depending on whether it has nested collections. If a collection has nested collections, it will be assigned the block style. Otherwise it will have the flow style.
如果想总是输出 block style(即后者),可以在 dump 时将 default_flow_style
参数置为 False
。
1
| yaml.dump(d, default_flow_style=False)
|
YAML → Python
yaml.load
该函数将 YAML 文档转换为 Python 对象。
**注意,对不受信任的数据直接进行转换会造成安全隐患,yaml.load
和 pickle.load
一样强大,可以调用任意 Python 函数。**对于这种情况,请使用 yaml.safe_load
。
1
2
3
4
5
6
7
8
| >>> yaml.load("""
... - Hesperiidae
... - Papilionidae
... - Apatelodidae
... - Epiplemidae
... """)
['Hesperiidae', 'Papilionidae', 'Apatelodidae', 'Epiplemidae']
|
yaml.load
可以传入 字节字符串、Unicode 字符串、二进制文件对象和文本文件对象。对于 字节字符串和文件,要求必须是 utf-8、utf-16-be或utf-16-le编码。yaml.load
通过检查字符串和文件开头的 BOM 来检测编码,如果没有 BOM 则认为是 utf-8 编码。
yaml.load_all
如果字符串或文件包含多个 YAML 块,则可以使用 yaml.load_all
函数加载全部。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| >>> documents = """
... ---
... name: The Set of Gauntlets 'Pauraegen'
... description: >
... A set of handgear with sparks that crackle
... across its knuckleguards.
... ---
... name: The Set of Gauntlets 'Paurnen'
... description: >
... A set of gauntlets that gives off a foul,
... acrid odour yet remains untarnished.
... ---
... name: The Set of Gauntlets 'Paurnimmen'
... description: >
... A set of handgear, freezing with unnatural cold.
... """
>>> for data in yaml.load_all(documents):
... print(data)
{'description': 'A set of handgear with sparks that crackle across its knuckleguards.\n',
'name': "The Set of Gauntlets 'Pauraegen'"}
{'description': 'A set of gauntlets that gives off a foul, acrid odour yet remains untarnished.\n',
'name': "The Set of Gauntlets 'Paurnen'"}
{'description': 'A set of handgear, freezing with unnatural cold.\n',
'name': "The Set of Gauntlets 'Paurnimmen'"}
|
构造任意类型 Python 对象
PyYAML允许构造任何类型的Python对象,如下,注意 None 和 Bool type 都由多种值转换而来。
1
2
3
4
5
6
7
8
9
10
11
12
| >>> yaml.load("""
... none: [~, null]
... bool: [true, false, on, off]
... int: 42
... float: 3.14159
... list: [LITE, RES_ACID, SUS_DEXT]
... dict: {hp: 13, sp: 5}
... """)
{'none': [None, None], 'int': 42, 'float': 3.1415899999999999,
'list': ['LITE', 'RES_ACID', 'SUS_DEXT'], 'dict': {'hp': 13, 'sp': 5},
'bool': [True, False, True, False]}
|
还可以使用标记 !!python/object
构建 Python 类的实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| >>> class Hero:
... def __init__(self, name, hp, sp):
... self.name = name
... self.hp = hp
... self.sp = sp
... def __repr__(self):
... return "%s(name=%r, hp=%r, sp=%r)" % (
... self.__class__.__name__, self.name, self.hp, self.sp)
>>> yaml.load("""
... !!python/object:__main__.Hero
... name: Welthyr Syxgon
... hp: 1200
... sp: 0
... """)
Hero(name='Welthyr Syxgon', hp=1200, sp=0)
|
yaml.safe_load
safe_load 只识别标准 YAML 语法的 tag,不解析 Python 对象。
如果输入含有 python 对象,则会报错。
Python → YAML
yaml.dump
yaml.dump
函数接受 Python 对象并生成 YAML 文档。
1
2
3
4
5
6
| >>> print yaml.dump({'name': 'Silenthand Olleander', 'race': 'Human',
... 'traits': ['ONE_HAND', 'ONE_EYE']})
name: Silenthand Olleander
race: Human
traits: [ONE_HAND, ONE_EYE]
|
该函数可接受第二个参数,该参数必须为文本文件对象或二进制文件对象。此时,函数将生成的 YAML 文档写入文件中,否则作为函数返回值返回。
除了普通的数据对象,PyYAML 也可以 dump Python 对象。
1
2
3
4
5
6
7
8
9
10
11
12
| >>> class Hero:
... def __init__(self, name, hp, sp):
... self.name = name
... self.hp = hp
... self.sp = sp
... def __repr__(self):
... return "%s(name=%r, hp=%r, sp=%r)" % (
... self.__class__.__name__, self.name, self.hp, self.sp)
>>> print(yaml.dump(Hero("Galain Ysseleg", hp=-3, sp=2)))
!!python/object:__main__.Hero {hp: -3, name: Galain Ysseleg, sp: 2}
|
yaml.dump_all
该函数将多个 yaml 文档 dump 到单个流中,此时传入列表或者生成器等可迭代对象。
1
2
3
4
5
6
7
| >>> print yaml.dump([1,2,3], explicit_start=True)
--- [1, 2, 3]
>>> print yaml.dump_all([1,2,3], explicit_start=True)
--- 1
--- 2
--- 3
|
自定义输出格式
yaml.dump
支持许多关键字参数,用于指定输出的格式详细信息。
width
设置行宽indent
设置缩进default_flow_style
默认使用 flow style,不使用 block style。default_style
设置字符串的表示符号,单引号还是双引号。
例如,设置行宽和缩进。
1
2
3
4
5
| >>> print(yaml.dump(range(50), width=50, indent=4))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
|
自定义 Python 对象转换
yaml.YAMLObject
你可以控制 Python 对象拥有自己的输出格式,最简单的方式是定义一个子类从 yaml.YAMLObject
继承。
yaml.YAMLObject
通过元类“魔法”,注册一个 constructor,用于转换 YAML 节点到 Python 类实例;一个 representer,用于转换 Python 类实例到 YAML 节点。
1
2
3
4
5
6
7
8
9
10
| >>> class Monster(yaml.YAMLObject):
... yaml_tag = u'!Monster'
... def __init__(self, name, hp, ac, attacks):
... self.name = name
... self.hp = hp
... self.ac = ac
... self.attacks = attacks
... def __repr__(self):
... return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
... self.__class__.__name__, self.name, self.hp, self.ac, self.attacks)
|
上面的类在 dump 时
1
2
3
4
5
6
7
8
| >>> print(yaml.dump(Monster(
... name='Cave lizard', hp=[3,6], ac=16, attacks=['BITE','HURT'])))
!Monster
ac: 16
attacks: [BITE, HURT]
hp: [3, 6]
name: Cave lizard
|
同时如果 load 一个 Monster 对象,YAML 格式为:
1
2
3
4
5
6
7
8
9
| >>> yaml.load("""
... --- !Monster
... name: Cave spider
... hp: [2,6] # 2d6
... ac: 16
... attacks: [BITE, HURT]
... """)
Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])
|
yaml.add_constructor 和 yaml.add_representer
如果不想使用元类继承的方式,也可以使用函数 yaml.add_constructor
注册构造函数,使用 yaml.add_repersenter
注册表现函数。
举例说明,我们有这样一个类
1
2
3
4
5
6
7
8
| >>> class Dice(tuple):
... def __new__(cls, a, b):
... return tuple.__new__(cls, [a, b])
... def __repr__(self):
... return "Dice(%s,%s)" % self
>>> print(Dice(3,6))
Dice(3,6)
|
默认情况下转换为 YAML 格式后,不够优雅
1
2
3
4
| >>> print(yaml.dump(Dice(3,6)))
!!python/object/new:__main__.Dice
- !!python/tuple [3, 6]
|
假如我们想让 Dice(3, 6)
在 YAML 中表示为 3d6
,可以这样:
1
2
3
4
5
6
7
| import yaml
def dice_representer(dumper, data):
return dumper.represent_scalar('!dice', '%sd%s' % data)
# 注册表现函数
yaml.add_representer(Dice, dice_representer)
|
此时再 dump 一个 Dice 对象
1
2
| >>> print(yaml.dump({'gold': Dice(10,6)}))
{gold: !dice '10d6'}
|
对于 load 我们也可以做相同的自定义:
1
2
3
4
5
6
| def dice_constructor(loader, node):
... value = loader.construct_scalar(node)
... a, b = map(int, value.split('d'))
... return Dice(a, b)
>>> yaml.add_constructor('!dice', dice_constructor)
|
此时再 load 一个 YAML 节点:
1
2
3
4
5
| >>> print(yaml.load("""
... initial hit points: !dice 8d4
... """))
{'initial hit points': Dice(8,4)}
|
yaml.add_implicit_resolver
如果你想在不指定 !dice
标记的情况下将 XdY
的格式转换为 Dice 类的对象,可以使用 add_implicit_resolver
。
1
2
3
| >>> import re
>>> pattern = re.compile(r'^\d+d\d+$')
>>> yaml.add_implicit_resolver(u'!dice', pattern)
|
这时转换就不再需要指定 !dice
。
1
2
3
4
5
6
7
8
9
| >>> print(yaml.dump({'treasure': Dice(10,20)}))
{treasure: 10d20}
>>> print(yaml.load("""
... damage: 5d10
... """))
{'damage': Dice(5,10)}
|
转换映射参考表
YAML tag | Python type |
---|
Standard YAML tags | |
!!null | None |
!!bool | bool |
!!int | int or long (int in Python 3) |
!!float | float |
!!binary | str (bytes in Python 3) |
!!timestamp | datetime.datetime |
!!omap , !!pairs | list of pairs |
!!set | set |
!!str | str or unicode (str in Python 3) |
!!seq | list |
!!map | dict |
Python-specific tags | |
!!python/none | None |
!!python/bool | bool |
!!python/bytes | (bytes in Python 3) |
!!python/str | str (str in Python 3) |
!!python/unicode | unicode (str in Python 3) |
!!python/int | int |
!!python/long | long (int in Python 3) |
!!python/float | float |
!!python/complex | complex |
!!python/list | list |
!!python/tuple | tuple |
!!python/dict | dict |
Complex Python tags | |
!!python/name:module.name | module.name |
!!python/module:package.module | package.module |
!!python/object:module.cls | module.cls instance |
!!python/object/new:module.cls | module.cls instance |
!!python/object/apply:module.f | value of f(...) |
参考链接