Python侧开28期-偕行-学习笔记-python常用第三方库

一、pymysql

1、操作流程

  1. 导入模块
  2. 连接数据库
  3. 创建游标对象
  4. 执行数据库操作
  5. 关闭游标对象
  6. 关闭数据库连接

2、连接和关闭数据库

  • PyMySQL 模块提供了四个连接数据库的函数名,其本质是多个变量的函数引用横等赋值,最终还是执行一个函数。
  • 官方代码: 按代码的赋值顺序,推荐使用 Connect 方式连接,其它函数使用方式相同。
Connect = connect = Connection = connections.Connection
  • 连接数据库:函数格式及主要参数如下:
Connect(host, port, user, password, database, charset)
* `host`:数据库主机的地址,默认为 `localhost`。
* `port`:数据库的端口号,默认为 `3306`。
* `user`:连接数据库所用的用户名。
* `password`:连接数据库所用的密码。
* `database`:要连接的数据库的名称。
* `charset`:连接数据库时使用的字符集,默认为"utf8"。
  • 关闭数据库:数据库使用完毕后,需要调用 close() 方法将数据库连接对象关闭,让其断开数据库连接,避免资源泄漏。
import pymysql

# 连接数据库
conn = pymysql.Connect(
    host="127.0.0.1",
    port=3306,
    user="root",
    password="123456",
    database="litemall",
    charset="utf8"
)

# 获取游标
cursor = conn.cursor()

# 执行语句
cursor.execute("select version()")

# 获取执行结果第一条数据
fetchone = cursor.fetchone()
print(fetchone)

# 关闭游标
cursor.close()
# 关闭数据库
conn.close()

3、 获取和关闭游标对象

  • 在进行数据库操作时,每次数据库操作都应该建立一个游标对象,在操作完毕后将其关闭,不应使用一个游标对象进行多次操作。

  • 游标对象的作用是用来执行数据库操作,在执行完一次操作后,应调用 close() 方法将游标对象关闭。

  • PyMySQL 模块使用 数据库连接对象.cursor() 方法获取游标对象。

4、执行SQL语句

  • PyMySQL 模块使用 execute() 方法执行 SQL 语句,实现数据库操作。
  • 语法格式:游标对象.execute(query, args=None)
    • query:需要执行的 SQL 语句;
    • args:为 SQL 语句中的占位符提供参数,防止 SQL 注入的发生;
insert_sql = ''' insert into class(name,sort,type) values(%s, %s, %s) '''
values = ("创业宝典", 9, 0)
result = cursor.execute(insert_sql,values)

5、查询操作

(1)获取单条查询记录fetchone()

  • 使用 execute 执行完毕 SQL 语句后, 可以使用 游标对象.fetchone() 方法获取一条查询结果。

  • fetchone() 方法可重复使用程序会继续向后读取查询结果如果无查询结果可读取,则返回 None

(2)获取所有查询记录fetchall()

  • 可以使用 游标对象.fetchall() 方法获取全部查询结果。

(3)获取指定条数查询结果fetchmany(size)`

  • 可以使用 游标对象.fetchmany(size) 方法获取指定数量查询结果。
import pymysql

# 连接数据库
conn = pymysql.connect(
    host="localhost",
    port=3306,
    user="root",
    password="123456",
    database="litemall",
    charset="utf8"
)

# 获取游标对象
cursor = conn.cursor()

# 执行查询sql语句
select_sql = "select name from litemall_goods where brand_id = %s;"# %s占位符
value = 1001000
cursor.execute(select_sql,value)

# 执行之后使用游标获取一条数据
one = cursor.fetchone()
# 再获取一条
two = cursor.fetchone()
print(f"第一条数据:{one}")
print(f"第二条数据:{two}")
print("*"*20)

# 获取5条查询记录
five = cursor.fetchmany(5)
print(f"前五条记录为:{five}")
print("*"*20)

# 获取全部查询记录
all = cursor.fetchall()
print(f"全部结果为:{all}")
print("*"*20)

# 关闭游标
cursor.close()
# 关闭数据库连接
conn.cursor()

6、插入操作

  • 执行插入操作同样使用 execute() 方法,插入数据时的 SQL 语句中的新数据,可以使用传参的方式填 入。

  • 参数可以使用 元组列表 形式传入。

  • 在插入数据操作完成后,需要对所做的更改操作进行提交

  • 提交操作使用数据库连接对象.commit() 方法。

import pymysql

# 连接数据库
conn = pymysql.connect(
    host="localhost",
    port=3306,
    user="root",
    password="123456",
    database="litemall"
)

# 获取游标
cursor = conn.cursor()

# 执行插入语句
insert_sql = "insert into litemall_goods(id,name,brief) values (%s,%s,%s);"
values = (1005001,"夏凉被","夏天冷到感冒")
result = cursor.execute(insert_sql,values)
print(f"执行插入语句后的结果是啥:{result}")# 结果是执行成功的条数

# 插入数据执行完之后需要对操作进行提交
conn.commit()

# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()

7、更新操作

  • 更新操作同插入操作,只是 SQL 语句不同,操作完毕后,需要执行提交操作。
import pymysql

# 连接数据库
conn = pymysql.connect(
    host="localhost",
    port=3306,
    user="root",
    password="123456",
    database="litemall"
)

# 获取游标
cursor = conn.cursor()

# 执行更新语句
update_sql = "update litemall_goods set name = %s,brief = %s where id = %s;"
# 列表参数
values = ["溜冰鞋","会滑冰的也会倒","1005001"]
result = cursor.execute(update_sql,values)
print(f"执行更新语句后的结果是啥:{result}")# 结果是执行成功的条数

# 更新数据执行完之后需要对操作进行提交
conn.commit()

# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()

8、删除操作

  • 删除操作同插入操作和更新操作,只是 SQL 语句不同,操作完毕后,需要执行提交操作
import pymysql

# 连接数据库
conn = pymysql.connect(
    host="localhost",
    port=3306,
    user="root",
    password="123456",
    database="litemall"
)

# 获取游标
cursor = conn.cursor()

# 执行插入语句
delete_sql = "delete from litemall_goods where id = %s;"
# 元组类型参数
values = (1005001,)
result = cursor.execute(delete_sql,values)
print(f"执行删除语句后的结果是啥:{result}")# 结果是执行成功的条数

# 删除数据执行完之后需要对操作进行提交
conn.commit()

# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()

二、PyYAML及yaml文件

1、什么是YAML文件

  • YAML全称其实是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写,所以它强调的是数据本身,而不是以标记为重点。

  • YAML 是一种可读性非常高,与程序语言数据结构非常接近。同时具备丰富的表达能力和可扩展性,并且易于使用的数据标记语言。

2、YAML的基本语法规则

  • 大小写敏感;
  • 使用缩进表示层级关系;
  • 缩进时不允许使用Tab键,只允许使用空格。(可以将你的ide的tab按键输出替换成4个空格);
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可;
  • 表示注释;

3、YAML 的数据结构

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary);
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list);
  • 纯量(scalars):单个的、不可再分的值;

4、对象类型

  • 对象的一组键值对,使用冒号结构表示,会转换成 Python 中的字典。
#Python对应 {'animals': 'dog'}
animals: dog 

# python对应 {'person': {'name': 'Tom', 'age': 20, 'gender': 'male'}}
person: {name: tom,age: 20,gender: male} 

5、数组类型

  • 数组类型使用 - 为前缀,每个元素独占一行,通过缩进关系表示层级包含关系,会转换成 Python 中的列表。
# python:['one', 'two', 'three', 'four', 'five']
- one
- two
- three
- fore
- five

# python:[[1, 2, 3], [4, 5, 6]]
-
  - 1
  - 2
  - 3
-
  - 4
  - 5
  - 6

6、纯量类型

  • 纯量类型是最基本的、不可再分的值;类似基本数据类型。

    • 字符串, 不需要使用双引号包裹
    • 布尔值,true,True,false,False都可以
    • 整数
    • 浮点数
    • 时间,时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区
    • 日期,日期必须使用ISO 8601格式,即 yyyy-MM-dd
    • Null,~ 表示 Null

YAML:

int: 12
float: 12.3
string: pets
bool: true
None: null
time: 2001-12-14t21:59:43.10-05:00
date: 2018-03-21

Python:

{
   'int': 12, 
   'float': 12.3, 
   'string': 'pets', 
   'bool': True, 
   'None': None, 
   'time': datetime.datetime(2001, 12, 14, 21, 59, 43, 100000, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=68400))), 
   'date': datetime.date(2018, 3, 21)
}

7、复杂结构

  • Tips:有 : 后面的内容就解析成字典,有 - 后面的内容就解析成列表的元素;

YAML:

cool_list:
  - 10
  - 15
  - 12

hard_list:
  - {key: value}
  - [1,2,3]
  - test:
      - 1
      - 2
      - 3

twice_list:
  -
    - {a: AA}
    - {b: BB}
    - {c: CC}

Python:

{
    'cool_list': [10, 15, 12],
    'hard_list': [
        {'key': 'value'},
        [1, 2, 3],
        {
            'test': [1, 2, 3]
        }
    ],
    'twice_list':[
        [
            {'a': 'AA'},
            {'b': 'BB'},
            {'c': 'CC'}
        ]
    ]
}

8、使用PyYAML进行YAML 文件处理

(1)安装PyYAML模块

pip install pyyaml

(2)读取 YAML 文件

  • YAML 模块使用 safe_load() 方法读取 yaml 文件,在读取文件之前,和普通文件一样,需要先将文件打开。
import yaml

# 读取yaml文件
with open("复杂结构.yaml","r") as file:
    result = yaml.safe_load(file)
print(result)
print(type(result))

(3)写入 YAML 文件

  • YAML 模块使用 safe_dump() 方法向 yaml 文件中写入数据,在写入文件之前,也需要先将文件打开。
import yaml

data = [
{
    "java":[{"基本数据类型":["字符串","数组","集合","map",]},["IO流","反射"]],
    "python": {"高级用法":["装饰器","注解"]}
},
    "python上手快",
    ["要学java","也要学python"]
]

with open("dump.yaml","a",encoding="utf-8") as file:
    # allow_unicode=True -- yaml文件显示中文
    result = yaml.safe_dump(data,file,allow_unicode=True)
print(result)
print(data)

结果:

- java:
  - 基本数据类型:
    - 字符串
    - 数组
    - 集合
    - map
  - - IO流
    - 反射
  python:
    高级用法:
    - 装饰器
    - 注解
- python上手快
- - 要学java
  - 也要学python

三、urllib3-TODO

四、openpyxl

提示: 如果pycharm无法识别excel文件(显示问号),那就安装插件Atom Material Icons

1、openpyxl安装

  • 安装:pip install openpyxl
  • 导入:import openpyxl

2、openpyxl读取excel的步骤

注意: openpyxl只支持读取xlsx格式的数据,在准备数据的时候新建文件就要建xlsx格式的,不能通过xls的去修改后缀名;

  • 读取工作簿
  • 读取工作表
  • 读取单元格
  • 获取单元格数据
import openpyxl

# 获取工作簿
book = openpyxl.load_workbook("exceldata.xlsx")

# 读取当前活跃工作表--活跃的意思关闭excel文件前一刻或者此时正打开的sheet窗口
sheet = book.active
print(f"sheet={sheet}")# sheet=<Worksheet "Sheet1">
print(f"sheet类型={type(sheet)}")# sheet类型=<class 'openpyxl.worksheet.worksheet.Worksheet'>

# 读取单个单元格
# 第一列第一行--方式一
cell_a1 = sheet['A1']
# A3第一列,第三行--方式二
cell_a3 = sheet.cell(column = 1,row = 3)
print(f"cell_a1={cell_a1}")# cell_a1=<Cell 'Sheet1'.A1>
print(f"cell_a1类型={type(cell_a1)}")# cell_a1类型=<class 'openpyxl.cell.cell.Cell'>

# 读取多个单元格,一个正方形区域--结尾为:元组数据,内部将一行也放入一个元组
cells_1 = sheet['A1':'C3']
print(f"cells={cells_1}")# cells=((<Cell 'Sheet1'.A1>, <Cell 'Sheet1'.B1>, <Cell 'Sheet1'.C1>), (<Cell 'Sheet1'.A2>, <Cell 'Sheet1'.B2>, <Cell 'Sheet1'.C2>), (<Cell 'Sheet1'.A3>, <Cell 'Sheet1'.B3>, <Cell 'Sheet1'.C3>))
print(f"cells类型={type(cells_1)}")# cells类型=<class 'tuple'>

# 获取单元格的值
cell_a1_value = cell_a1.value
print(f"cell_a1_value={cell_a1_value}")# cell_a1_value=1
print(f"cell_a1_value类型={type(cell_a1_value)}")

3、其他api

  • 获取所有工作表名:book.sheetnames

  • 指定某张工作表:

    • 通过工作表名指定:current_sheet = book["表二数据"]
    • 通过工作表索引指定:current_sheet = book.worksheets[0]
  • 读取列表的行数、列数、行列数;

import openpyxl

# 获取工作簿
book = openpyxl.load_workbook("exceldata.xlsx")

# 获取所有工作表名
sheet_names = book.sheetnames
print(f"sheet_name={sheet_names}")# sheet_name=['表一数据', '表二数据', 'Sheet3']

# 通过工作表名指定工作表
current_sheet = book["表二数据"]
# 通过工作表索引指定工作表--索引默认从0开始
current_sheet = book.worksheets[0]

print(current_sheet['A1'].row)        #读取的表格的行数
print(current_sheet['A1'].column)     #读取的表格的列数
print(current_sheet['A8'].coordinate) #读取的表格的行列数。输出的值为'A8'

4、工具封装

from openpyxl import load_workbook

def get_excel(filename,sheetname):
    """
    提取数据,格式:[(,,),(,,),(,,)]
    :param filename: excel文件名
    :param sheetname: excel工作表名
    :return: 
    """
    # 读取工作簿
    book = load_workbook(filename)
    # 打开指定名称工作表
    sheet = book[sheetname]

    # 提取数据,格式:[(,,),(,,),(,,)]
    result = []
    for row in sheet:# 遍历表得到每一行
        print(f"这是表的什么:{row}")
        temp = []
        for cell in row:# 获取每一行的每一个cell
            print(f"这又是什么:{cell}")
            value = cell.value
            temp.append(value)
        result.append(tuple(temp))

    return result

if __name__ == '__main__':
    filename = "exceldata.xlsx"
    sheetname = "表二数据"
    result = get_excel(filename,sheetname)
    print(result)
---------------------------------------------------------------
这是表的什么:(<Cell '表二数据'.A1>, <Cell '表二数据'.B1>, <Cell '表二数据'.C1>)
这又是什么:<Cell '表二数据'.A1>
这又是什么:<Cell '表二数据'.B1>
这又是什么:<Cell '表二数据'.C1>
这是表的什么:(<Cell '表二数据'.A2>, <Cell '表二数据'.B2>, <Cell '表二数据'.C2>)
这又是什么:<Cell '表二数据'.A2>
这又是什么:<Cell '表二数据'.B2>
这又是什么:<Cell '表二数据'.C2>
这是表的什么:(<Cell '表二数据'.A3>, <Cell '表二数据'.B3>, <Cell '表二数据'.C3>)
这又是什么:<Cell '表二数据'.A3>
这又是什么:<Cell '表二数据'.B3>
这又是什么:<Cell '表二数据'.C3>
[('A-1', 'B-1', 'C-1'), ('A-2', 'B-2', 'C-2'), ('A-3', 'B-3', 'C-3')]

五、pandas-TODO

六、mitmproxy-mock工具与定制化

1、mitmproxy介绍

  • 工具一:mitmproxy:交互式的命令行工具;
    • 注意:不支持 windows;
  • 工具二:mitmweb:基于浏览器的界面交互工具;
  • 工具三:mitmdump:简单的终端输出,可以编写强大的插件和脚本;
  • mitmproxy官网

2、mitmproxy第三方库安装

注意:

  1. python版本最好在3.8以上;
    2.库的名称是mitmproxy,但是模块却使用的是mitmdump
# 方式一
pip install mitmproxy==5.2.0

# 方式二
pip install pipx
pipx install mitmproxy==5.2.0

# 验证是否安装成功
mitmdump --version

3、PC 端证书配置

  1. 配置电脑代理;
  2. 启动 mitmproxy;
  3. 在系统自带浏览器输入地址 mitm.it
  4. 选择对应系统下载证书并安装;

1. 配置电脑代理;

2. 启动 mitmproxy;

  • 启动mitmproxy服务命令:mitmdump

3. 在系统自带浏览器输入地址 mitm.it

4. 选择对应系统下载证书并安装;

4、移动端证书配置

  1. 在手机配置代理,ip 配置为电脑的 ip 地址,端口配置为 mitmproxy 监听端口8080;
  2. 启动 mitmproxy;
  3. 在手机浏览器输入地址 mitm.it;
  4. 选择 Android,下载并安装,即可成功抓取手机端的 https 的数据包。

5、mitmdump 使用-结合python脚本

(1)mitmdump常用参数

(2)mitmdump核心组件

(3)脚本初体验

from mitmproxy import http

def request(flow: http.HTTPFlow):
    """
    重写request方法,意思是需要对请求信息进行mock
    :param flow: 这就是mitmproxy抓取到的请求,通过参数注解的方式指定为HTTPFlow类型
    :return:
    """
    # 添加自定义的请求头信息
    flow.request.headers['myheader'] = 'echo'
    # 打印所有的请求头信息
    print(flow.request.headers)

6、mitmdump 具体使用

(1)实现 MapLocal

  • 原理:监听到请求之后直接返回设置好的本地文件数据,直接拦截请求,请求没有发送到服务器;
  • 步骤:
  1. 创建一个本地文件,设定响应数据;
  2. 编写脚本重写request()方法,在请求事件中,即给响应对象复制;
  3. 执行命令启动mitmdump:mitmdump -p 9989 -s E:\hogwartscode\mitmproxyproject\xueqiu_maplocal.py
  4. 访问浏览器(app)验证结果;
"""
对雪球行情中的数据进行maplocal处理
url=https://stock.xueqiu.com/v5/stock/screener/quote/list.json
"""
from mitmproxy import http

# request 名称不能被改变
# mitmdump 加载这个脚本的时候,当有请求来的时候,就会回调这个request方法
# flow 为抓取到的请求信息
def request(flow: http.HTTPFlow) -> None:
    print("flow=",flow)
    print("*"*20)
    print("type(flow)=",type(flow))
    # 发起请求,判断url是不是等于一个路径
    if "https://stock.xueqiu.com/v5/stock/screener/quote/list.json" in flow.request.pretty_url:
        # 打开本地maplocal数据文件
        file = "E:/hogwartscode/mitmproxyproject/xuqiu_maplocal.json"
        with open(file,'r',encoding="utf8") as f:
            # 一旦请求的url等于我们的预期,我们就会创造一个response:status=200,内容为本地数据文件,数据格式为json
            flow.response = http.Response.make(
                200,  # status状态码
                f.read(),  # content需要是bytes类型
                # 请求头信息,字典形式
                {"Content-Type": "application/json"}# --数据类型为json

            )

image

(2)实现 Rewrite

  • 原理:请求发送到服务器返回的时候,篡改返回的内容;
  • 实现:重写response()方法,获取到响应数据之后进行修改再返回;
"""
需求:对雪球行情页股票名称和换手率数据进行修改
"""
import json
from mitmproxy.http import HTTPFlow

def response(flow: HTTPFlow):
    if "https://stock.xueqiu.com/v5/stock/screener/quote/list.json" in flow.request.pretty_url:
        print("响应内容:", flow.response.content)# 类容是bytes类型
        content = json.loads(flow.response.content)
        print("content====",content)
        # 从响应内容中获取第一支股票数据
        items_1 = content["data"]["list"][0]
        # 股票名称
        items_1["name"] = "花好月圆夜"
        # 换手率
        items_1["turnover_rate"] = 9527

        # 响应的数据是bytes类型,所以mock数据之后需要把数据转成bytes类型
        # 注意:这里返回的变量不再是content,而是text
        flow.response.text = json.dumps(content)

image

7、Pydantic

(1)pydantic介绍

Pydantic 是一个使用Python类型注解进行数据验证和管理的模块。

pip install pydantic

它类似于 Django DRF 序列化器的数据校验功能,不同的是,Django里的序列化器的Field是有限制的,如果你想要使用自己的Field还需要继承并重写它的基类:

# Django 序列化器
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    author = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)

而 Pydantic 基于Python3.7以上的类型注解特性,实现了可以对任何类做数据校验的功能:

# Pydantic 数据校验功能
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: Optional[datetime] = None
    friends: List[int] = []


external_data = {
    'id': '123',
    'signup_ts': '2019-06-01 12:22',
    'friends': [1, 2, '3'],
}
user = User(**external_data)
print(user.id)
print(type(user.id))
#> 123
#> <class 'int'>
print(repr(user.signup_ts))
#> datetime.datetime(2019, 6, 1, 12, 22)
print(user.friends)
#> [1, 2, 3]
print(user.dict())
"""
{
    'id': 123,
    'signup_ts': datetime.datetime(2019, 6, 1, 12, 22),
    'friends': [1, 2, 3],
    'name': 'John Doe',
}
"""

从上面的基本使用可以看到,它甚至能自动帮你做数据类型的转换,比如代码中的 user.id, 在字典中是字符串,但经过Pydantic校验器后,它自动变成了int型,因为User类里的注解就是int型。

当我们的数据和定义的注解类型不一致时会报这样的Error:

from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: Optional[datetime] = None
    friends: List[int] = []


external_data = {
    'id': '123',
    'signup_ts': '2019-06-01 12:222',
    'friends': [1, 2, '3'],
}
user = User(**external_data)
"""
Traceback (most recent call last):
  File "1.py", line 18, in <module>
    user = User(**external_data)
  File "pydantic\main.py", line 331, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for User
signup_ts
  invalid datetime format (type=value_error.datetime)
"""

即 “invalid datetime format”, 因为我传入的 signup_ts 不是标准的时间格式(多了个2)。

(2)Pydantic 模型数据导出

通过Pydantic模型中自带的 json 属性方法,能让经过校验后的数据一行命令直接转成 json 字符串,如前文中的user对象:

print(user.dict())  # 转为字典
"""
{
    'id': 123,
    'signup_ts': datetime.datetime(2019, 6, 1, 12, 22),
    'friends': [1, 2, 3],
    'name': 'John Doe',
}
"""
print(user.json())  # 转为json
"""
{"id": 123, "signup_ts": "2019-06-01T12:22:00", "friends": [1, 2, 3], "name": "John Doe"}
"""

非常方便。它还支持将整个数据结构导出为 schema json,它能完整地描述整个对象的数据结构类型:

print(user.schema_json(indent=2))
"""
{
  "title": "User",
  "type": "object",
  "properties": {
    "id": {
      "title": "Id",
      "type": "integer"
    },
    "signup_ts": {
      "title": "Signup Ts",
      "type": "string",
      "format": "date-time"
    },
    "friends": {
      "title": "Friends",
      "default": [],
      "type": "array",
      "items": {
        "type": "integer"
      }
    },
    "name": {
      "title": "Name",
      "default": "John Doe",
      "type": "string"
    }
  },
  "required": [
    "id"
  ]
}
"""

(3)数据导入

除了直接定义数据校验模型,它还能通过ORM、字符串、文件导入到数据校验模型:

  • 比如字符串(raw):
from datetime import datetime
from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: datetime = None
      
m = User.parse_raw('{"id": 123, "name": "James"}')
print(m)
#> id=123 signup_ts=None name='James'
  • 此外,它能直接将ORM的对象输入,转为Pydantic的对象,比如从Sqlalchemy ORM:
from typing import List
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.declarative import declarative_base
from pydantic import BaseModel, constr

Base = declarative_base()


class CompanyOrm(Base):
    __tablename__ = 'companies'
    id = Column(Integer, primary_key=True, nullable=False)
    public_key = Column(String(20), index=True, nullable=False, unique=True)
    name = Column(String(63), unique=True)
    domains = Column(ARRAY(String(255)))


class CompanyModel(BaseModel):
    id: int
    public_key: constr(max_length=20)
    name: constr(max_length=63)
    domains: List[constr(max_length=255)]

    class Config:
        orm_mode = True


co_orm = CompanyOrm(
    id=123,
    public_key='foobar',
    name='Testing',
    domains=['example.com', 'foobar.com'],
)
print(co_orm)
#> <models_orm_mode.CompanyOrm object at 0x7f0bdac44850>
co_model = CompanyModel.from_orm(co_orm)
print(co_model)
#> id=123 public_key='foobar' name='Testing' domains=['example.com',
#> 'foobar.com']
  • 从Json文件导入:
from datetime import datetime
from pathlib import Path
from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: datetime = None
      
path = Path('data.json')
path.write_text('{"id": 123, "name": "James"}')
m = User.parse_file(path)
print(m)
  • 从pickle导入:
import pickle
from datetime import datetime
from pydantic import BaseModel

pickle_data = pickle.dumps({
    'id': 123,
    'name': 'James',
    'signup_ts': datetime(2017, 7, 14)
})
m = User.parse_raw(
    pickle_data, content_type='application/pickle', allow_pickle=True
)
print(m)
#> id=123 signup_ts=datetime.datetime(2017, 7, 14, 0, 0) name='James'

(4)自定义数据校验

你还能给它增加 validator 装饰器,增加你需要的校验逻辑:

from pydantic import BaseModel, ValidationError, validator


class UserModel(BaseModel):
    name: str
    username: str
    password1: str
    password2: str

    @validator('name')
    def name_must_contain_space(cls, v):
        if ' ' not in v:
            raise ValueError('must contain a space')
        return v.title()

    @validator('password2')
    def passwords_match(cls, v, values, **kwargs):
        if 'password1' in values and v != values['password1']:
            raise ValueError('passwords do not match')
        return v

    @validator('username')
    def username_alphanumeric(cls, v):
        assert v.isalnum(), 'must be alphanumeric'
        return v

上面,我们增加了三种自定义校验逻辑:

1.name 必须带有空格

2.password2 必须和 password1 相同

3.username 必须为字母

让我们试试这三个校验是否成功实现:

user = UserModel(
    name='samuel colvin',
    username='scolvin',
    password1='zxcvbn',
    password2='zxcvbn',
)
print(user)
#> name='Samuel Colvin' username='scolvin' password1='zxcvbn' password2='zxcvbn'

try:
    UserModel(
        name='samuel',
        username='scolvin',
        password1='zxcvbn',
        password2='zxcvbn2',
    )
except ValidationError as e:
    print(e)
    """
    2 validation errors for UserModel
    name
      must contain a space (type=value_error)
    password2
      passwords do not match (type=value_error)
    """

可以看到,第一个UserModel里的数据完全没有问题,通过校验。

第二个UserModel里的数据,由于name存在空格,password2和password1不一致,无法通过校验。

(5)性能表现

Pydantic 比 Django-rest-framework 还快了12.3倍:

Package Version Relative Performance Mean validation time
pydantic 1.7.3 93.7μs
attrs + cattrs 20.3.0 1.5x slower 143.6μs
valideer 0.4.2 1.9x slower 175.9μs
marshmallow 3.10.0 2.4x slower 227.6μs
voluptuous 0.12.1 2.7x slower 257.5μs
trafaret 2.1.0 3.2x slower 296.7μs
schematics 2.1.0 10.2x slower 955.5μs
django-rest-framework 3.12.2 12.3x slower 1148.4μs
cerberus 1.3.2 25.9x slower 2427.6μs