目录

PyYAML笔记

最近使用PyYAML处理YAML文件,做个记录。

关于Node

在PyYAML中,Node作为YAML信息模型的实体,有三种类型:

  • 标量scalar ScalarNode(tag, value, style, start_mark, end_mark)
  • 序列sequence SequenceNode(tag, value, flow_style, start_mark, end_mark)
  • 映射mapping MappingNode(tag, value, flow_style, start_mark, end_mark)

node由Composer产生,通过Serializer可将其序列化为YAML流。

示例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
content = """
title: 这是一个标题
categories:
- 分类1
tags:
- 标签1
- 标签2
"""

node = yaml.compose(content)
print(node)
print('')
stream = yaml.serialize(node)
print(stream)

输出:

1
2
3
4
5
6
7
8
MappingNode(tag='tag:yaml.org,2002:map', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='title'), ScalarNode(tag='tag:yaml.org,2002:str', value='这是一个标题')), (ScalarNode(tag='tag:yaml.org,2002:str', value='categories'), SequenceNode(tag='tag:yaml.org,2002:seq', value=[ScalarNode(tag='tag:yaml.org,2002:str', value='分类1')])), (ScalarNode(tag='tag:yaml.org,2002:str', value='tags'), SequenceNode(tag='tag:yaml.org,2002:seq', value=[ScalarNode(tag='tag:yaml.org,2002:str', value='标签1'), ScalarNode(tag='tag:yaml.org,2002:str', value='标签2')]))])

title: "\u8FD9\u662F\u4E00\u4E2A\u6807\u9898"
categories:
- "\u5206\u7C7B1"
tags:
- "\u6807\u7B7E1"
- "\u6807\u7B7E2"

关于constructor和representer

constructor之于Loading,representer之于Dumping,二者功能相反。

constructor

用于将一个YAML节点转换为Python对象。

yaml.add_constructor(tag, constructor, Loader=Loader)为目标tag指定constructor;constructor的参数包括一个Loader实例和一个YAML节点,返回值是一个Python对象。

Loader提供了获取YAML节点值的方法(该值用于构造Python对象):

  • Loader.construct_scalar(node)

    验证目标YAML节点是标量节点scalar node并返回其值

  • Loader.construct_sequence(node)

    验证目标YAML节点是序列节点sequence node并返回其值

  • Loader.construct_mapping(node)

    验证目标YAML节点是映射节点mapping node并返回其值

示例代码:

 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
class Post(object):
    def __init__(self, title, categories, tags):
        self.title = title
        self.categories = categories
        self.tags = tags

def post_constructor(loader, node):
    mapping_value = loader.construct_mapping(node)
    return Post(mapping_value['title'], mapping_value['categories'], mapping_value['tags'])


yaml.add_constructor(u'!Post', post_constructor)	
res = yaml.load("""
!Post
title: 这是一个标题
categories:
- 分类1
tags:
- 标签1
- 标签2
""")
print(type(res))
print(res.title)
print(res.categories)
print(res.tags)

输出:

1
2
3
4
<class '__main__.Post'>
这是一个标题
['分类1']
['标签1', '标签2']

representer

用于将一个Python对象转换为YAML节点。

yaml.add_representer(data_type, representer, Dumper=Dumper)为指定类型的对象指定representer;representer的参数包含一个Dumper实例和一个Python对象,返回值是一个YAML节点。

Dumper提供了构造YAML节点的方法:

  • Dumper.represent_scalar(tag, value, style=None)

    返回具有指定tag和value的标量节点scalar node

  • Dumper.represent_sequence(tag, sequence, flow_style=None)

    返回具有指定tag和value的序列节点sequence node

  • Dumper.represent_mapping(tag, mapping, flow_style=None)

    返回具有指定tag和value的映射节点mapping node

示例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Post(object):
    def __init__(self, title, categories, tags):
        self.title = title
        self.categories = categories
        self.tags = tags

def post_representer(dumper, post):
    attrs = []
    attrs.append(('title', post.title))
    attrs.append(('categories', post.categories))
    attrs.append(('tags', post.tags))
    return dumper.represent_mapping(u'!Post', attrs)

post = Post('这是一个标题', ['分类1'], ['标签1', '标签2'])
yaml.add_representer(Post, post_representer)
res = yaml.dump(post, allow_unicode=True)
print(res)

输出:

1
2
3
4
5
6
7
!Post
title: 这是一个标题
categories:
- 分类1
tags:
- 标签1
- 标签2

可以看到输出内容带有类型tag,如果希望不附带tag,需使用YAML标准类型,在本例中dumper.represent_mapping(u'tag:yaml.org,2002:map', attrs)

YAML标准类型tag可以参考 https://yaml.org/type/index.html

关于yaml.dump()参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dump(data, stream=None, Dumper=Dumper,
    default_style=None,
    default_flow_style=None,
    encoding='utf-8', # encoding=None (Python 3)
    explicit_start=None,
    explicit_end=None,
    version=None,
    tags=None,
    canonical=None,
    indent=None,
    width=None,
    allow_unicode=None,
    line_break=None)

已知参数用法:

  • allow_unicode=True 正常显示Unicode字符,比如中文

  • default_flow_style=True 紧凑输出为一行

  • canonical=TrueCanonical Form格式输出

关于Tag

YAML使用tag标识数据类型,全局tag采用tag:起始的URL格式,本地tag采用!起始的格式,格式说明参考下面官方文档:

1
2
3
4
5
6
7
Tag property: # Usually unspecified.
    none    : Unspecified tag (automatically resolved by application).
    '!'     : Non-specific tag (by default, "!!map"/"!!seq"/"!!str").
    '!foo'  : Primary (by convention, means a local "!foo" tag).
    '!!foo' : Secondary (by convention, means "tag:yaml.org,2002:foo").
    '!h!foo': Requires "%TAG !h! <prefix>" (and then means "<prefix>foo").
    '!<foo>': Verbatim tag (always means "foo")

其中,!!tag:yaml.org,2002:的缩写,dump内容所包含的tag采用缩写格式。


参考文档