python-第三方库-[yarl、yaml]

python-第三方库-[yarl、yaml]

  • [一: yarl](#一: yarl)
    • [1> yarl 介绍](#1> yarl 介绍)
    • [2> yarl.URL 介绍](#2> yarl.URL 介绍)
      • [1. yarl.URL 的语法格式](#1. yarl.URL 的语法格式)
      • [2. yarl.URL demo](#2. yarl.URL demo)
      • [3. yarl.URL.build()](#3. yarl.URL.build())
      • [4. yarl.URL().with_*()](#4. yarl.URL().with_*())
      • [5. yarl.URL().update_query()](#5. yarl.URL().update_query())
      • [6> url / &%组合](#6> url / &%组合)
  • 二:yaml
    • [1> yaml 介绍](#1> yaml 介绍)
    • [2> yaml 基本规则](#2> yaml 基本规则)
    • [3> yaml demo](#3> yaml demo)
      • [1. yaml.load](#1. yaml.load)
      • [2. yaml.load Unicode&yaml.Loader](#2. yaml.load Unicode&yaml.Loader)
      • [3. yaml.dump&Unicode](#3. yaml.dump&Unicode)
      • [4. yaml.load_all](#4. yaml.load_all)
      • [5. yaml 与编码对象class](#5. yaml 与编码对象class)
      • [6. yaml 多个编码对象class](#6. yaml 多个编码对象class)
      • [7. yaml.safe_load](#7. yaml.safe_load)
    • [4> yaml 三大组件](#4> yaml 三大组件)
        • [1. 构造器、表示器、解析器 介绍](#1. 构造器、表示器、解析器 介绍)
        • [2. 构造&转存](#2. 构造&转存)
        • [3. 序列化构造器](#3. 序列化构造器)
        • [4. 自定义构造器](#4. 自定义构造器)
    • [5> yaml引用环境变量](#5> yaml引用环境变量)
    • [6> libyaml](#6> libyaml)
    • [7> FAQ](#7> FAQ)
      • [1. yaml.safe_load&load区别](#1. yaml.safe_load&load区别)

一: yarl

1> yarl 介绍

URL介绍 :统一资源定位符,它是用来表示互联网上的某个资源地址,互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它;
yarl :yarl 库是一个强大的工具,用于处理 URL(统一资源定位符);
yarl作用:提供了简单且灵活的 API,使得 URL 的解析、构建和操作变得简单;

2> yarl.URL 介绍

from yarl import URL

1. yarl.URL 的语法格式

URL 的语法格式:
protocol://hostname[:port]/path[?query][#fragment]
part 含义 注释
protocol 指网络传输协议 以下是经常用到的几个协议; http,通过 HTTP 协议访问该资源,格式 http://; https,通过安全的 HTTPS 协议访问该资源, 格式 https://; file,资源是本地计算机上的文件,格式 file:// ftp,通过 FTP访问资源,格式 FTP://
hostname 主机名 指存放资源的服务器的域名、主机名或 IP 地址。有时,在主机名前也可以包含连接到服务器所需的用户名和密码(格式:username:password@hostname)
port 端口号 port 是一个可选的整数,它的取值范围 是 0-65535。如果 port 被省略时就使用默认端口,各种传输协议都有默认的端口号,如 http 的默认端口为 80,https 的端口是 443;
path 路由地址 由零个或多个/符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。路由地址决定了服务端如何处理这个请求;
query 查询 从?开始到#为止,它们之间的部分就是参数,又称搜索部分或者查询字符串。这个部分允许有多个参数,参数与参数之间用&作为分隔符;下面demo 为:spm=1011.2124.3001.5298
fragment 信息片断 它用于指定网络资源中的片断,例如一个网页中有多个名词解释,那么可使用 fragment 直接对它们定位

2. yarl.URL demo

from yarl import URL

url_string = "https://mp.csdn.net/mp_blog/manage/article?spm=1011.2124.3001.5298"
url = URL(url_string)

print(url)
print(url.scheme)
print(url.host)
print(url.port)
print(url.authority)

print(url.path)
print(url.query)
print(url.query_string)

print(url.user)
print(url.password)
=======================
https://mp.csdn.net/mp_blog/manage/article?spm=1011.2124.3001.5298
https
mp.csdn.net
443
mp.csdn.net:443
/mp_blog/manage/article
<MultiDictProxy('spm': '1011.2124.3001.5298')>
spm=1011.2124.3001.5298

None
None

3. yarl.URL.build()

from yarl import URL

url = URL.build(
          scheme='https',
          host='mp.csdn.net',
          port=443,
          path='/mp_blog/manage/article',
          query={'spm': '1011.2124.3001.5298'}
          )
print(url)
===============
https://mp.csdn.net:443/mp_blog/manage/article?spm=1011.2124.3001.5298

4. yarl.URL().with_*()

with_scheme、with_user 、with_password 、with_host 、with_port、with_path、with_query、with_fragment、with_name

with_*() 单步骤构建

from yarl import URL

url = URL.build(
          scheme='https',
          host='mp.csdn.net',
          port=443,
          path='/mp_blog/manage/article',
          )
query = {'spm': '1011.2124.3001.5298'}

print(url.with_query(query))

with_().with_()多步骤构建url

from yarl import URL

url = URL.build(
          scheme='https',
          host='mp.csdn.net',
          port=443,
          )
print(url)
query = {'spm': '1011.2124.3001.5298'}
print(url.with_path('/mp_blog/manage/article').with_query(query))
============================
https://mp.csdn.net:443
https://mp.csdn.net:443/mp_blog/manage/article?spm=1011.2124.3001.5298

5. yarl.URL().update_query()

url.with_query(query):清除之前的query 信息;

from yarl import URL

url = URL.build(
          scheme='https',
          host='mp.csdn.net',
          port=443,
          path='/mp_blog/manage/article',
          query = {'spm': '1011.2124.3001.5298'}
          )

query = {'xxx': '127.0.0.1', '***': '128.0.0.1'}
print(url.with_query(query))
===================
https://mp.csdn.net:443/mp_blog/manage/article?xxx=127.0.0.1&***=128.0.0.1

url.update_query(query):对之前存在信息进行更新;

from yarl import URL

url = URL.build(
          scheme='https',
          host='mp.csdn.net',
          port=443,
          path='/mp_blog/manage/article',
          query = {'spm': '1011.2124.3001.5298'}
          )

query = {'xxx': '127.0.0.1', '***': '128.0.0.1'}
print(url.update_query(query))
=============================
https://mp.csdn.net:443/mp_blog/manage/article?spm=1011.2124.3001.5298&xxx=127.0.0.1&***=128.0.0.1

6> url / &%组合

from yarl import URL
url = (
URL('https://mp.csdn.net')/
'mp_blog/manage/article'%
{'spm': '1011.2124.3001.5298'}
)

print(url)
=====================
https://mp.csdn.net/mp_blog/manage/article?spm=1011.2124.3001.5298

二:yaml

1> yaml 介绍

Yaml :Yaml 是一种人类可读的数据序列化语言,通常用于存储项目中的配置文件;例如 docker 和 k8s 都使用 Yaml 文件定义部署信息;类似于 json 数据,但 Yaml 的格式更加丰富,可以存储更复杂的数据结构

2> yaml 基本规则

规则 说明
层级关系 yaml 的层级关系通过缩进表示,缩进空格数没有规定,但同层级的缩进必须一致。建议全局使用相同的缩进空格数;
存储数据 yaml 使用键值对的方式存储数据。 - 在键值对的值前面添加 - 符号表示这个键的值是一个列表

1. 数据结构介绍

YAML中,有3种基本的数据结构:Scalars(标量)、Sequences(序列)和Mappings(映射)

数据结构 说明 举例
Scalars 标量是单个的、不可再分的值。标量可以是字符串、数字、布尔值、null、日期等 1. 普通样式、单/双引号样式 2. 字面样式(literal ) 3. 折叠样式(folded)
序列(Sequences) 序列是一组按次序排列的值,每一个元素都可以是任意数据类型 连字符-开头的行构成一个序列; - A - B - C ==> key: [A, B, C]
Mappings(映射) 映射是一种键值对的集合,其中每个键都必须唯一 每个键值对用冒号+一个空格分隔(key: value)
普通样式、单/双引号样式
import yaml

document = """
爱好1: 喝酒
      唱歌
爱好2: '喝酒

      唱歌'
"""

res = yaml.load(document, Loader=yaml.Loader)
print(res)
============
{'爱好1': '喝酒 唱歌', '爱好2': '喝酒\n唱歌'}
字面样式(literal )
import yaml

document = """
爱好1: |
      喝酒
      唱歌
爱好2: '喝酒

      唱歌'
"""

res = yaml.load(document, Loader=yaml.Loader)
print(res)
=====================
{'爱好1': '喝酒\n唱歌\n', '爱好2': '喝酒\n唱歌'}
折叠样式(folded)
import yaml

document = """
爱好1: >
      喝酒
      唱歌
爱好2: '喝酒

      唱歌'
"""

res = yaml.load(document, Loader=yaml.Loader)
print(res)
==============
{'爱好1': '喝酒 唱歌\n', '爱好2': '喝酒\n唱歌'}
Mappings(映射)
import yaml

document = """
爱好1: 喝酒
爱好2: 唱歌
"""
res = yaml.load(document, Loader=yaml.Loader)
print(res)
============
{'爱好1': '喝酒', '爱好2': '唱歌'}

2. 语法锚点和引用

编程中我们通常用变量引用来解决这种问题,YAML也提供了一种类似的机制实现变量引用,它就是锚点引用(Anchors and Aliases);例如:同的数据被重复使用, 配置信息,

定义锚点&引用锚点
import yaml

document = """
key: &anchor pig
key2: *anchor
key3: *anchor
"""
'''
1. 使用 & 符号可以定义一个锚点
2. * 符号+锚点名 可以引用先前定义的锚点
'''
res = yaml.load(document, Loader=yaml.Loader)
print(res)
=================
{'key': 'pig', 'key2': 'pig', 'key3': 'pig'}
锚定映射&序列节点
import yaml

document = """
# 定义一个共享的内容
common_data: &anchor_id
  name: Common Name
  value: Common Value
 
# 使用锚定引用共享内容
nodes:
  - *anchor_id
  - *anchor_id
 
"""

'''
1. common_data: &anchor_id: 是一个锚定的名字,它指向一个映射(即键值对的集合)
2. *anchor_id 是一个别名,用来引用先前定义的那个映射
3. nodes 是一个list, 引用anchor_id
'''
res = yaml.load(document, Loader=yaml.Loader)
print(res)

=================
{'common_data': {'name': 'Common Name', 'value': 'Common Value'}, 'nodes': [{'name': 'Common Name', 'value': 'Common Value'}, {'name': 'Common Name', 'value': 'Common Value'}]}
合并映射-merge key

YAML提供了一种<<语法用于合并映射(Mappings)。它允许你将一个映射的键值对合并到另一个映射中,从而实现映射之间的继承和合并。这种语法也被称为"merge key";

提高代码的可维护性和可读性。这对于定义共享的配置或基本设置,并在派生配置中进行定制化非常有用

import yaml

document = """
base: &tmp_base
  name: John
  age: 30
extension:
  <<: *tmp_base
  age: 31
  city: New York 
"""

# 在base基础上update extension 信息

res = yaml.load(document, Loader=yaml.Loader)
print(res)
===================
{'base': {'name': 'John', 'age': 30}, 'extension': {'name': 'John', 'age': 31, 'city': 'New York'}}

3. yaml tag

AML中,标签(Tags)用于对数据进行类型标识或自定义标识。它们提供了一种扩展YAML数据模型的方式;

tag 介绍
tag 说明 demo
!!str 标识字符串类型 name: !!str John
!!int 标识整数类型 count: !!int 10
!!float 标识浮点数类型 pi: !!float 3.1415926
!!bool 标识布尔类型 is_valid: !!bool true
!!null 标识null data: !!null
隐式tag 隐式声明 boolean: !!bool "true" integer: !!int "3"
显式tag 显式声明 boolean: true integer: 3
自定义tag
yaml tag python type
!!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

3> yaml demo

配置文件用 .yaml 或者 .py 形式如下

1. yaml.load

import yaml

document = """
conv_001:
    op_list:
        - Conv2d
        - Conv2d
    creat_params:
        selector: 
            - F
            - F
        layer1:
            in_channels: 1
            out_channels: 1
            kernel_size: 3
            stride: 2
            padding: 1
            groups: 1
        layer2:
            in_channels: 1
            out_channels: 2
            kernel_size: 3
            stride: 4
            padding: 1
            groups: 1
    quant_params:
        data_num: 1
        inputShape: 
            - [1, 1, 224, 8]
"""

res = yaml.load(document, Loader=yaml.Loader)
print(res)
print(type(res))
=======================
{'conv_001': {'op_list': ['Conv2d', 'Conv2d'], 'creat_params': {'selector': ['F', 'F'], 'layer1': {'in_channels': 1, 'out_channels': 1, 'kernel_size': 3, 'stride': 2, 'padding': 1, 'groups': 1}, 'layer2': {'in_channels': 1, 'out_channels': 2, 'kernel_size': 3, 'stride': 4, 'padding': 1, 'groups': 1}}, 'quant_params': {'data_num': 1, 'inputShape': [[1, 1, 224, 8]]}}}
<class 'dict'>

2. yaml.load Unicode&yaml.Loader

使用 Loader=yaml.Loader 实现Unicode

import yaml

document = """
姓名: 小明,
爱好:
    - 喝酒
    - 唱歌
    - 赌牌
    - 打游戏
"""

res = yaml.load(document, Loader=yaml.Loader)
print(res)
print(type(res))

3. yaml.dump&Unicode

import yaml
import os

document = """
姓名: 小明,
爱好:
    - 喝酒
    - 唱歌
    - 赌牌
    - 打游戏
"""

res = yaml.load(document, Loader=yaml.Loader)
document_name = f'document.yaml'
with open(document_name, 'w') as file:
    yaml.dump(res, file, default_flow_style=False, encoding='utf-8', allow_unicode=True)
'''
数据中包含中文等 Unicode 字符,yaml.dump() 会将它们编码成不可读的表示方式。为了解决这个问题,
可以添加三个关键字参数,使中文等 Unicode 字符以可读形式保存,default_flow_style, encoding, allow_unicode
'''
print(os.path.exists(document_name))
with open(document_name, 'r') as f:
    res_load = yaml.load(f, Loader=yaml.Loader)
print(res_load)
===================
True
{'姓名': '小明,', '爱好': ['喝酒', '唱歌', '赌牌', '打游戏']}

4. yaml.load_all

aml文档中,使用 --- 符号可以分段。对于带有分段的Yaml文档,使用 yaml.load() 函数会报错,需要使用 yaml.load_all() 函数;

import yaml

document = """
姓名: 小明,
爱好:
    - 喝酒
    - 唱歌
    - 赌牌
    - 打游戏
---
姓名: 小红,
爱好:
    - 写字
    - 看书
"""

res = yaml.load_all(document, Loader=yaml.Loader)
print(type(res))
for i in res:
    print(i)
====================
<class 'generator'>
{'姓名': '小明,', '爱好': ['喝酒', '唱歌', '赌牌', '打游戏']}
{'姓名': '小红,', '爱好': ['写字', '看书']}

5. yaml 与编码对象class

import yaml

class Anmails:
    def __init__(self, name, likes):
        self.name = name
        self.likes = likes
    def __repr__(self):
        return f'{self.__class__.__name__}(name={self.name}, like={self.likes})'
    def like(self):
        print(f'我是{self.name},我喜欢{str(self.likes)}! ')

to_do = Anmails('猪', ['喝酒', '唱歌', '赌牌', '打游戏'])
document = yaml.dump(to_do)
like_A = yaml.load(document, Loader=yaml.Loader)
print(yaml.dump(to_do))
print(like_A)
print(type(like_A))
like_A.like()
=======================
!!python/object:__main__.Anmails
likes:
- "\u559D\u9152"
- "\u5531\u6B4C"
- "\u8D4C\u724C"
- "\u6253\u6E38\u620F"
name: "\u732A"

Anmails(name=猪, like=['喝酒', '唱歌', '赌牌', '打游戏'])
<class '__main__.Anmails'>
我是猪,我喜欢['喝酒', '唱歌', '赌牌', '打游戏']!

6. yaml 多个编码对象class

import yaml

class Anmails:
    def __init__(self, name, likes):
        self.name = name
        self.likes = likes
    def __repr__(self):
        return f'{self.__class__.__name__}(name={self.name}, like={self.likes})'
    def like(self):
        print(f'我是{self.name},我喜欢{str(self.likes)}! ')

to_do1 = Anmails('猪', ['喝酒', '唱歌', '赌牌', '打游戏'])
to_do2 = Anmails('de-lovely', ['写字', '看书'])
document_name = r'document.yaml'
with open(document_name, 'w') as f:
    yaml.dump_all([to_do1, to_do2], f, default_flow_style=False, encoding='utf-8', allow_unicode=True)


==============
document.yaml:
!!python/object:__main__.Anmails
likes:
- 喝酒
- 唱歌
- 赌牌
- 打游戏
name: 猪
--- !!python/object:__main__.Anmails
likes:
- 写字
- 看书
name: de-lovely

7. yaml.safe_load

import yaml

document_name = 'document.yaml'
with open(document_name, 'r') as f:
    # case_params = yaml.load(f, Loader=yaml.Loader)
    case_params = yaml.safe_load(f)
print(case_params)

#修改参数
case_params['conv_001']['creat_params']['layer1']['groups'] = 2
case_params['conv_001']['op_list'][0] = 'Conv3d'
#保存参数
with open('document_currect.yaml', 'w') as file:
    yaml.dump(case_params, file)

4> yaml 三大组件

1. 构造器、表示器、解析器 介绍
组件 功能 描述
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 的标签识别功能,使其能够处理更多的数据类型和标签;
2. 构造&转存
import yaml

class Introduce(yaml.YAMLObject):
    yaml_tag = u'!Introduce'

    def __init__(self, name, likes):
        self.name = name
        self.likes = likes

    def __repr__(self):
        return f'{self.__class__.__name__}(name={self.name}, {self.likes})'


document = """
!Introduce
name: xiaoming
likes:
    - games
"""
'''
1. !Introduce: 定义一个YAML标签用于解析和生成Monster类型数据
2. yaml.dump(res): 转储Monster对象为YAML格式数据
'''
res = yaml.load(document, Loader=yaml.Loader)
print(res)
print(type(res))
document_cp = yaml.dump(res)
print(document_cp)
=================
Introduce(name=xiaoming, ['games'])
<class '__main__.Introduce'>
!Introduce
likes:
- games
name: xiaoming
3. 序列化构造器
import yaml

class Dice(tuple):
    def __new__(cls, a, b):
        return tuple.__new__(cls, [a, b])

    def __repr__(self):
        return "Dice(%s,%s)" % self

def dice_representer(dumper, data):
    return dumper.represent_scalar(u'!dice', u'%sd%s' % data)

yaml.add_representer(Dice, dice_representer)
res = yaml.dump(Dice(3,6))
print(res)
============
!dice '3d6'


无:# yaml.add_representer(Dice, dice_representer)
=========================
!!python/object/new:__main__.Dice
- !!python/tuple
  - 3
  - 6
4. 自定义构造器
import yaml

class Dice(tuple):
    def __new__(cls, a, b):
        return tuple.__new__(cls, [a, b])

    def __repr__(self):
        return "Dice(%s,%s)" % self

def dice_representer(dumper, data):
    return dumper.represent_scalar(u'!dice', u'%sd%s' % data)

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)

res = yaml.load("initial hit points: !dice 8d4", yaml.Loader)
print(res)
==========
{'initial hit points': Dice(8,4)}

5> yaml引用环境变量

import yaml
import os

def render_env_constructor(loader, node):
    value = loader.construct_scalar(node)
    return os.path.expandvars(value)

yaml.SafeLoader.add_constructor('!ENV', render_env_constructor)

os.environ["a"]="111"
os.environ["b"]="222"
res = yaml.safe_load("test: !ENV a=${a}, b=$b")
#加载YAML时自动替换了标签!ENV标识标量的环境变量占位符
print(res)
=======
{'test': 'a=111, b=222'}

环境变量通常都是字符串形式。如果你使用了数字、布尔等其他类型,需要解析后手动转换。

os.path.expandvars是一个内置函数,用于展开字符串中的环境变量。它会在目标字符串中查找形式为 V A R 或 VAR或 VAR或{VAR}的环境变量引用,并将其替换为响应的环境变量的值。如果找不到匹配的环境变量,那么引用保持不变

6> libyaml

libyaml 介绍
libyaml库 libyaml是一个独立的C库,用于处理 yaml数据的解析和生成。Pyyaml 可以使用 LibYAML 作为其解析和生成 yaml 数据的底层引擎;
特点 LibYAML 具有高性能、低内存占用、加速 yaml数据处理
组件 提供 CLoader 和 CDumper 两个组件用来集成 LibYAML; CLoader: PyYAML 的解析器(Loader)的C扩展版本 CDumper: PyYAML的生成器(Dumper)的C扩展版本
CLoader和CDumper优势 1. 通过与LibYAML的集成,提供了更高效的解析和生成功能; 2. 处理大型YAML数据时获得更好的性能和效率

7> FAQ

1. yaml.safe_load&load区别

load: Loader支持加载任意的Python对象,可能导致执行任意 Python 代码

safe_load:SafeLoader 限制了加载过程中执行的操作,只允许加载基本类型的对象,如字符串、整数、列表和字典等;当你处理来自不受信任的源或不确定性的 YAML 文件时,特别是从外部来源加载时,使用 SafeLoader 是一个良好的实践,可以提高安全性并防止可能的代码注入或其他潜在的安全问题