以下介绍为chatgpt生成。
PyYAML 是一个用于解析和生成 YAML 数据的 Python 库。它提供了简单易用的接口,用于读取和写入 YAML 格式的文件、字符串或流。
以下是一些关于 PyYAML 的重要特点和功能:
使用 PyYAML,你可以轻松地在 Python 中处理 YAML 数据,无论是读取和解析现有的 YAML 文件,还是将数据转换为 YAML 格式并进行写入。这使得 PyYAML 成为处理配置文件、数据交换、持久化存储等场景中的有用工具。
以下是一个简单示例,展示了如何使用 PyYAML 加载和保存 YAML 文件:
import yaml
# 加载 YAML 文件
with open('config.yaml', 'r') as file:
config = yaml.safe_load(file)
# 修改配置数据
config['database']['host'] = 'localhost'
config['database']['port'] = 3306
# 保存配置到 YAML 文件
with open('config.yaml', 'w') as file:
yaml.dump(config, file)
在上述示例中,我们首先使用 safe_load 函数加载 YAML 文件。然后,我们对配置数据进行修改,并使用 dump 函数将修改后的数据保存回 YAML 文件。
总的来说,PyYAML 提供了一种简单和方便的方式来处理 YAML 数据,并与 Python 的数据类型和对象进行交互。
在YAML中,有3种基本的数据结构:Scalars(标量)、Sequences(序列)和Mappings(映射)。它们用于构建和表示复杂的数据。
标量是单个的、不可再分的值。标量可以是字符串、数字、布尔值、null、日期等。下面是一些标量数据类型的例子:
boolean:
- TRUE #true, True都可以
- FALSE #false, False都可以
float:
- 3.14
- 6.8523015e+5 #可以使用科学计数法
int:
- 123
- 0b1010_0111_0100_1010_1110 #二进制表示
null: ~ #使用~或者null表示null
string:
- 哈哈
- 'Hello world' #可以使用双引号或者单引号包裹特殊字符
- newline
newline2 #字符串可以拆成多行, 每一行会被转化成一个空格
date:
- 2018-02-17 #日期必须使用ISO 8601格式, 即yyyy-MM-dd
datetime:
- 2018-02-17T15:02:31+08:00 #时间使用ISO 8601格式, 时间和日期之间使用T连接, 最后使用+代表时区
对于多行字符串标量,YAML有多种表示方式:
lines: line1
line2
line3
line4
line5
lines: 'line1
line2
line3
line4
line5'
转换为python对象:
{'lines': 'line1 line2 line3\nline4 line5'}
转换时,去掉缩进,换行->空格,空行->换行。
lines: |
line1
line2
line3
line4
line5
转换为python对象:
{'lines': 'line1\n line2\nline3\n\nline4\nline5\n'}
转换时,保留缩进和换行
lines: >
line1
line2
line3
line4
line5
转换为python对象:
{'lines': 'line1\n line2\nline3\nline4 line5\n'}
转换时,保留缩进和换行。但相邻、无缩进的两个非空行,会合并成一行,并用一个空格字符分隔。
- >
line1
序列是一组按次序排列的值,每一个元素都可以是任意数据类型。
以连字符-
开头的行构成一个序列:
- A
- B
- C
序列的行内表示:
key: [value1, value2, ...]
映射是一种键值对的集合,其中每个键都必须唯一。
每个键值对用冒号+一个空格分隔(key: value
)
key:
child_key1: value1
child_key2: value2
映射的行内表示:
key: {child_key1: value1, child_key2: value2}
我们在编写yaml时,会遇到一些相同的数据被重复使用,比如一些配置信息如用户名、邮箱、数据库配置等。如果这类数据发生变更,我们就需要修改很多处,维护很麻烦。
在编程中我们通常用变量引用来解决这种问题,YAML也提供了一种类似的机制实现变量引用,它就是锚点引用(Anchors and Aliases)。
在 YAML 中,锚点引用是一种可以引用先前定义的数据节点的机制。锚点引用允许你在 YAML 文件中重用相同的数据节点,避免了数据的重复定义,提高了可维护性和可读性。通过给节点添加一个锚点(使用 &
符号),然后使用别名(使用 *
符号)引用该锚点,可以将值重复使用。
使用 &
符号可以定义一个锚点。锚点定义在数据节点的位置,并且可以在 YAML 文件中的其他位置引用该锚点。例如:
key: &anchor value
如上,&anchor
了一个锚点,名为anchor,相当于value这个数据节点的别名。
使用 *
符号+锚点名 可以引用先前定义的锚点。引用锚点时,你可以在 YAML 文件的其他位置使用锚点的名称。例如:
key2: *anchor
key3: *anchor
在上述示例中,*anchor
引用了先前定义的锚点,这样 key2 和 key3 的值将与 &anchor
定义的数据节点相同。
person: &person_anchor
name: Jane
age: 18
tasks:
- &task1_anchor task1
- task2
YAML提供了一种<<
语法用于合并映射(Mappings)。它允许你将一个映射的键值对合并到另一个映射中,从而实现映射之间的继承和合并。这种语法也被称为"merge key"。
合并映射需要结合锚点引用来使用,例如:
base: &base
name: John
age: 30
extension:
<<: *base
age: 31
city: New York
转换成python对象:
{'base': {'name': 'John', 'age': 30}, 'extension': {'name': 'John', 'age': 31, 'city': 'New York'}}
使用 <<
语法,你可以避免在多个映射中重复定义相同的键值对,从而提高代码的可维护性和可读性。这对于定义共享的配置或基本设置,并在派生配置中进行定制化非常有用。
需要注意的是,<<
语法只适用于映射之间的合并,而不适用于序列(Sequences)或标量(Scalars)。
在YAML中,标签(Tags)用于对数据进行类型标识或自定义标识。它们提供了一种扩展YAML数据模型的方式。
在 PyYAML 中,标签是用于标识和处理 YAML 数据节点的类型的特殊注释。PyYAML 支持一些内置标签,用于标识常见的数据类型。以下是一些常用的 PyYAML 标签的介绍:
# 标识字符串类型
name: !!str John
# 标识整数类型
count: !!int 10
# 标识浮点数类型
pi: !!float 3.1415926
# 标识布尔类型
is_valid: !!bool true
# 标识null
data: !!null
这些标签用于标识不同的数据类型,帮助解析器和处理程序正确地理解和处理 YAML 数据。标签的使用是可选的,因为 YAML 解析器可以根据数据的结构和内容自动识别大多数常见的数据类型。
对于PyYAML而言,标签可以是隐式的或显式的。
显式声明的如:
boolean: true
integer: 3
float: 3.14
隐式的如:
boolean: !!bool "true"
integer: !!int "3"
float: !!float "3.14"
它们都被转换为相同的python对象:
{'boolean': True, 'integer': 3, 'float': 3.14}
在PyYAML中,没有显式定义标签的标量,都服从隐式标签解析。隐式标签解析会根据一组正则表达式来检查标量,如果其中一个表达式匹配,就会分配对应的标签给标量。
除了内置标签外,你还可以定义自己的标签,用于标识自定义类型或特殊处理。这允许你在加载和转储 YAML 数据时进行定制化操作。例如:
!CustomType
- item1
- item2
比如 PyYAML 就定义了很多!!python/
开头的自定义标签,用来帮助解析和生成python对象数据。
下面这张表描述了PyYAML如何将带有不同标签的数据节点转换为python对象。
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 |
在 PyYAML 中,Constructors(构造器)、Representers(表示器)和Resolvers(解析器)是用于处理 YAML 数据的重要组件。它们的主要作用如下:
Constructors(构造器):
Constructors 是用于将 YAML 数据解析为 Python 对象的组件。它们负责将 YAML 数据的不同类型转换为相应的 Python 对象。PyYAML 提供了一些内置的构造器,用于处理常见的数据类型,如字符串、整数、浮点数、布尔值等。同时,你也可以自定义构造器,以便将 YAML 数据解析为自定义的 Python 类型。通过注册构造器,你可以扩展 PyYAML 的解析功能,使其能够处理更多的数据类型。
Representers(表示器):
与构造器相反,Representers 是用于将 Python 对象表示为 YAML 数据的组件。它们负责将 Python 对象转换为 YAML 中的相应表示形式。PyYAML 提供了一些内置的表示器,用于处理常见的 Python 对象类型,如字符串、整数、浮点数、布尔值等。同时,你也可以自定义表示器,以便将自定义的 Python 类型表示为 YAML 数据。通过注册表示器,你可以定制 PyYAML 的转储功能,使其能够生成符合特定需求的 YAML 数据。
Resolvers(解析器):
Resolvers 是用于解析 YAML 数据中的标签(Tags)的组件。它们负责识别标签并将其映射到相应的构造器和表示器。PyYAML 提供了内置的标签解析器,用于处理常见的标准类型。同时,你也可以自定义解析器,以便识别和处理自定义的标签。通过注册解析器,你可以扩展 PyYAML 的标签识别功能,使其能够处理更多的数据类型和标签。
让我们通过两个自定义类型例子来理解这三大组件。
假如我们有一个 python 类型 Monster,现在需要自定义一个YAML标签用于解析和生成Monster类型数据。
最简单的实现方式是让 Monster 继承 yaml.YAMLObject
yaml.YAMLObjectclass 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)
定义如上这个Monster类,已经足够让PyYAML自动加载和转储 Monster 对象了。
1、从 YAML 加载 Monster对象:
yaml.load("""
!Monster
name: Cave spider
hp: [2, 6]
ac: 16
attacks: [BITE, HURT]
""", yaml.Loader)
执行结果:
Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])
2、转储Monster对象为YAML格式数据:
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
我们知道,PyYAML解析和表示YAML数据,都需要对应数据类型的构造器和表示器。那为什么我们仅仅是定义了一个Monster类,就能够解析和表示这种数据类型呢?难道在定义Monster类的时候PyYAML自动帮我们创建了Monster类型的构造器和表示器?
没错,正是这样的。我们知道元类的__init__()
方法只在每个类被定义时调用一次。而PyYAML正利用了这种元类机制,在我们定义Monster继承yaml.YAMLObject时,触发元类的__init__()
方法,为当前类的所有yaml_loader自动生成、注册了Monster的构造器和表示器。
源码如下:
# YAML基础模型类
class YAMLObject(metaclass=YAMLObjectMetaclass):
"""
An object that can dump itself to a YAML stream
and load itself from a YAML stream.
"""
__slots__ = () # no direct instantiation, so allow immutable subclasses
# 设置当前YAMLObject模型类使用的Loader和Dumper, 这些Loader和Dumper会自动注册构造器和表示器
yaml_loader = [Loader, FullLoader, UnsafeLoader]
yaml_dumper = Dumper
...
# YAML基础模型类的元类,自动注册构造器和表示器
class YAMLObjectMetaclass(type):
"""
The metaclass for YAMLObject.
"""
def __init__(cls, name, bases, kwds):
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
# 为Loader注册当前YAML模型类的构造器
if isinstance(cls.yaml_loader, list):
for loader in cls.yaml_loader:
loader.add_constructor(cls.yaml_tag, cls.from_yaml)
else:
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
# 为Dumper注册当前YAML模型类的表示器
cls.yaml_dumper.add_representer(cls, cls.to_yaml)
PyYAML给我们提供了 yaml.add_constructor 和 yaml.add_representer 两个函数,用来手动注册自定义构造器和表示器。
假如我们需要加载和转储自定义类型 Dice
:
class Dice(tuple):
def __new__(cls, a, b):
return tuple.__new__(cls, [a, b])
def __repr__(self):
return "Dice(%s,%s)" % self
让我们先看看PyYAML的默认转储YAML格式:
yaml.dump(Dice(3,6))
执行结果:
!!python/object/new:__main__.Dice
- !!python/tuple
- 3
- 6
看起来不太美好。假如我们想将它序列化为 AdB
的格式,即yaml.dump(Dice(3,6))
的结果为3d6
首先我们要定义一个表示器,用于将Dice对象转换成带有!dict
标签的标量节点:
def dice_representer(dumper, data):
return dumper.represent_scalar(u'!dice', u'%sd%s' % data)
然后将表示器注册到默认的Dumper:
yaml.add_representer(Dice, dice_representer)
再次尝试转储Dice对象:
yaml.dump(Dice(3,6))
转储结果符合预期:
!dice '3d6'
自定义构造器并注册到Loader、FullLoader和UnsafeLoader
def dice_constructor(loader, node):
value = loader.construct_scalar(node)
a, b = map(int, value.split('d'))
return Dice(a, b)
yaml.add_constructor(u'!dice', dice_constructor)
让我们加载YAML数据试试:
yaml.load("initial hit points: !dice 8d4", yaml.Loader)
加载结果符合预期:
{'initial hit points': Dice(8,4)}
我们可能希望自定义的标签也能像内置标签一样,被隐式的识别和解析。比如我们在YAML中不指定!dice
标签,而是把所有XdY
格式的未标记标量,都识别成!dice
并自动调用构造器解析。这时候我们可以添加一个解析器来实现:
import re
pattern = re.compile(r'^\d+d\d+$')
yaml.add_implicit_resolver(u'!dice', pattern)
再次尝试转储Dice对象,发现不再显示!dice
标签:
>>> yaml.dump({"treasure": Dice(10, 20)})
'treasure: 10d20\n'
加载YAML数据,也能隐式解析XdY格式的标量、加载成Dice对象:
>>> yaml.load("damage: 5d10", yaml.Loader)
{'damage': Dice(5,10)}
我们在编写YAML文件时,可能会希望能够引用环境变量,以提供更灵活的配置选项、或提高配置安全性。
我们可以通过自定义标签,标识环境变量类型,并自定义这个标签的构造器,以替换环境变量。
自定义替换环境变量的构造器并注册到SafeLoader(或是其他Loader)
def render_env_constructor(loader, node):
value = loader.construct_scalar(node)
return os.path.expandvars(value)
yaml.SafeLoader.add_constructor('!ENV', render_env_constructor)
让我们来测试效果:
import os
os.environ["a"]="111"
os.environ["b"]="222"
yaml.safe_load("test: !ENV a=${a}, b=$b")
测试成功!在加载YAML时自动替换了标签!ENV
标识标量的环境变量占位符:
{'test': 'a=111, b=222'}
注意:
os.path.expandvars
是一个内置函数,用于展开字符串中的环境变量。它会在目标字符串中查找形式为$VAR
或${VAR}
的环境变量引用,并将其替换为响应的环境变量的值。如果找不到匹配的环境变量,那么引用保持不变。LibYAML是一个独立的C库,用于处理 YAML 数据的解析和生成。PyYAML 可以使用 LibYAML 作为其解析和生成 YAML 数据的底层引擎。LibYAML 具有高性能和低内存占用的特点,可以加速 YAML 数据的处理。
PyYAML 提供 CLoader 和 CDumper 两个组件用来集成 LibYAML:
CLoader和CDumper是PyYAML提供的优化组件,它们通过与LibYAML的集成,提供了更高效的解析和生成功能。使用CLoader和CDumper可以在处理大型YAML数据时获得更好的性能和效率。
CLoader 和 CDumper 是可选组件,在使用它们之前需要下载安装 LibYAML: (PyYAML 6.x 似乎不需要)
# 下载源码包
wget http://pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz
# 解压
tar xf yaml-0.2.5.tar.gz
cd yaml-0.2.5/
# 配置构建。检查系统环境、依赖项和其他设置,生成适合系统的 Makefile 文件
./configure
# 编译源码并构建软件。根据 Makefile 中的指令,执行编译、链接等操作,生成可执行文件或库文件。
make
# 将构建好的软件安装到系统中。
# 将可执行文件、库文件和其他必要的文件复制到指定的目标位置,使软件可以在系统中被使用和访问。
make install
安装好 LibYAML 后,再下载 PyYAML 的源码包python setup.py --with-libyaml install
重新安装。(究极麻烦,告辞!)
使用 CLoader 和 CDumper:
from yaml import load, dump
try:
from yaml import CLoader as Loader, CDumper as Dumper
except ImportError:
from yaml import Loader, Dumper
# ...
data = load(stream, Loader=Loader)
# ...
output = dump(data, Dumper=Dumper)
ruamel.yaml Docs
ruamel.yaml是基于 PyYAML 3.11的扩展库,提供了额外的功能和改进。
这是一个功能丰富且兼容性较好的 YAML 库,支持 YAML 1.2 标准,并提供了对保留注释、保留标签、自定义类型等高级功能的支持。它可以与 PyYAML 代码兼容,并具有更好的性能和一些额外的功能。
ruamel.yaml和PyYAML的差异
本文介绍了Python的YAML解析库PyYAML,它的概念、简单使用、自定义三大组件,以及自定义实现PyYAML加载时渲染环境变量。还扩展了一些YAML的概念和主要语法,如标量、序列、映射和锚点引用。为大家理解和使用 PyYAML 尽绵薄之力~
感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。
CSDN大礼包:全网最全《Python学习资料》免费赠送!(安全链接,放心点击)
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python兼职渠道推荐*
学的同时助你创收,每天花1-2小时兼职,轻松稿定生活费.
三、最新Python学习笔记
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、实战案例
纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
CSDN大礼包:全网最全《Python学习资料》免费赠送!(安全链接,放心点击)
若有侵权,请联系删除