python库pydantic的简易入门教程
作者:青火_ 发布时间:2022-06-27 14:05:28
一、简介
pydantic 库是 python 中用于数据接口定义检查与设置管理的库。
pydantic 在运行时强制执行类型提示,并在数据无效时提供友好的错误。
它具有如下优点:
与 IDE/linter 完美搭配,不需要学习新的模式,只是使用类型注解定义类的实例
多用途,BaseSettings 既可以验证请求数据,也可以从环境变量中读取系统设置
快速
可以验证复杂结构
可扩展,可以使用validator装饰器装饰的模型上的方法来扩展验证
数据类集成,除了BaseModel,pydantic还提供了一个dataclass装饰器,它创建带有输入数据解析和验证的普通 Python 数据类。
二、安装
pip install pydantic
要测试 pydantic 是否已编译,请运行:
import pydantic
print('compiled:', pydantic.compiled)
支持使用dotenv文件获取配置,需要安装 python-dotenv
pip install pydantic[dotenv]
三、常见模型
pydantic中定义对象都是通过模型的,你可以认为模型就是类型语言中的类型。
1、BaseModel 基本模型
from pydantic import BaseModel
class User(BaseModel):
id: int
name = 'Jane Doe'
上面的例子,定义了一个User模型,继承自BaseModel,有2个字段,id是一个整数并且是必需的,name是一个带有默认值的字符串并且不是必需的
实例化使用:
user = User(id='123')
实例化将执行所有解析和验证,如果有错误则会触发 ValidationError 报错。
模型具有以下属性:
dict() 模型字段和值的字典
json() JSON 字符串表示dict()
copy() 模型的副本(默认为浅表副本)
parse_obj() 使用dict解析数据
parse_raw 将str或bytes并将其解析为json,然后将结果传递给parse_obj
parse_file 文件路径,读取文件并将内容传递给parse_raw。如果content_type省略,则从文件的扩展名推断
from_orm() 从ORM 对象创建模型
schema() 返回模式的字典
schema_json() 返回该字典的 JSON 字符串表示
construct() 允许在没有验证的情况下创建模型
__fields_set__ 初始化模型实例时设置的字段名称集
__fields__ 模型字段的字典
__config__ 模型的配置类
2、递归模型
可以使用模型本身作为注释中的类型来定义更复杂的数据结构。
from typing import List
from pydantic import BaseModel
class Foo(BaseModel):
count: int
size: float = None
class Bar(BaseModel):
apple = 'x'
banana = 'y'
class Spam(BaseModel):
foo: Foo
bars: List[Bar]
3、GenericModel 通用模型(泛型):
使用 typing.TypeVar 的实例作为参数,传递给 typing.Generic,然后在继承了pydantic.generics.GenericModel 的模型中使用:
from typing import Generic, TypeVar, Optional, List
from pydantic import BaseModel, validator, ValidationError
from pydantic.generics import GenericModel
DataT = TypeVar('DataT')
class Error(BaseModel):
code: int
message: str
class DataModel(BaseModel):
numbers: List[int]
people: List[str]
class Response(GenericModel, Generic[DataT]):
data: Optional[DataT]
error: Optional[Error]
@validator('error', always=True)
def check_consistency(cls, v, values):
if v is not None and values['data'] is not None:
raise ValueError('must not provide both data and error')
if v is None and values.get('data') is None:
raise ValueError('must provide data or error')
return v
data = DataModel(numbers=[1, 2, 3], people=[])
error = Error(code=404, message='Not found')
print(Response[int](data=1))
#> data=1 error=None
print(Response[str](data='value'))
#> data='value' error=None
print(Response[str](data='value').dict())
#> {'data': 'value', 'error': None}
print(Response[DataModel](data=data).dict())
"""
{
'data': {'numbers': [1, 2, 3], 'people': []},
'error': None,
}
"""
print(Response[DataModel](error=error).dict())
"""
{
'data': None,
'error': {'code': 404, 'message': 'Not found'},
}
"""
try:
Response[int](data='value')
except ValidationError as e:
print(e)
"""
2 validation errors for Response[int]
data
value is not a valid integer (type=type_error.integer)
error
must provide data or error (type=value_error)
"""
4、create_model 动态模型
在某些情况下,直到运行时才知道模型的结构。为此 pydantic 提供了create_model允许动态创建模型的方法。
from pydantic import BaseModel, create_model
DynamicFoobarModel = create_model('DynamicFoobarModel', foo=(str, ...), bar=123)
四、常用类型
None,type(None)或Literal[None]只允许None值
bool 布尔类型
int 整数类型
float 浮点数类型
str 字符串类型
bytes 字节类型
list 允许list,tuple,set,frozenset,deque, 或生成器并转换为列表
tuple 允许list,tuple,set,frozenset,deque, 或生成器并转换为元组
dict 字典类型
set 允许list,tuple,set,frozenset,deque, 或生成器和转换为集合;
frozenset 允许list,tuple,set,frozenset,deque, 或生成器和强制转换为冻结集
deque 允许list,tuple,set,frozenset,deque, 或生成器和强制转换为双端队列
datetime 的date,datetime,time,timedelta 等日期类型
typing 中的 Deque, Dict, FrozenSet, List, Optional, Sequence, Set, Tuple, Union,Callable,Pattern等类型
FilePath,文件路径
DirectoryPath 目录路径
EmailStr 电子邮件地址
NameEmail 有效的电子邮件地址或格式
PyObject 需要一个字符串并加载可在该虚线路径中导入的 python 对象;
Color 颜色类型
AnyUrl 任意网址
SecretStr、SecretBytes 敏感信息,将被格式化为'**********'或''
Json 类型
PaymentCardNumber 支付卡类型
约束类型,可以使用con*类型函数限制许多常见类型的值
conlist
item_type: Type[T]: 列表项的类型
min_items: int = None: 列表中的最小项目数
max_items: int = None: 列表中的最大项目数
conset
item_type: Type[T]: 设置项目的类型
min_items: int = None: 集合中的最小项目数
max_items: int = None: 集合中的最大项目数
conint
strict: bool = False: 控制类型强制
gt: int = None: 强制整数大于设定值
ge: int = None: 强制整数大于或等于设定值
lt: int = None: 强制整数小于设定值
le: int = None: 强制整数小于或等于设定值
multiple_of: int = None: 强制整数为设定值的倍数
confloat
strict: bool = False: 控制类型强制
gt: float = None: 强制浮点数大于设定值
ge: float = None: 强制 float 大于或等于设定值
lt: float = None: 强制浮点数小于设定值
le: float = None: 强制 float 小于或等于设定值
multiple_of: float = None: 强制 float 为设定值的倍数
condecimal
gt: Decimal = None: 强制十进制大于设定值
ge: Decimal = None: 强制十进制大于或等于设定值
lt: Decimal = None: 强制十进制小于设定值
le: Decimal = None: 强制十进制小于或等于设定值
max_digits: int = None: 小数点内的最大位数。它不包括小数点前的零或尾随的十进制零
decimal_places: int = None: 允许的最大小数位数。它不包括尾随十进制零
multiple_of: Decimal = None: 强制十进制为设定值的倍数
constr
strip_whitespace: bool = False: 删除前尾空格
to_lower: bool = False: 将所有字符转为小写
strict: bool = False: 控制类型强制
min_length: int = None: 字符串的最小长度
max_length: int = None: 字符串的最大长度
curtail_length: int = None: 当字符串长度超过设定值时,将字符串长度缩小到设定值
regex: str = None: 正则表达式来验证字符串
conbytes
strip_whitespace: bool = False: 删除前尾空格
to_lower: bool = False: 将所有字符转为小写
min_length: int = None: 字节串的最小长度
max_length: int = None: 字节串的最大长度
严格类型,您可以使用StrictStr,StrictBytes,StrictInt,StrictFloat,和StrictBool类型,以防止强制兼容类型
五、验证器
使用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
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
第二个参数始终是要验证的字段值,可以随意命名
单个验证器可以通过传递多个字段名称来应用于多个字段,也可以通过传递特殊值在所有字段上调用单个验证器'*'
关键字参数pre将导致在其他验证之前调用验证器
通过each_item=True将导致验证器被施加到单独的值(例如List,Dict,Set等),而不是整个对象
from typing import List
from pydantic import BaseModel, ValidationError, validator
class ParentModel(BaseModel):
names: List[str]
class ChildModel(ParentModel):
@validator('names', each_item=True)
def check_names_not_empty(cls, v):
assert v != '', 'Empty strings are not allowed.'
return v
# This will NOT raise a ValidationError because the validator was not called
try:
child = ChildModel(names=['Alice', 'Bob', 'Eve', ''])
except ValidationError as e:
print(e)
else:
print('No ValidationError caught.')
#> No ValidationError caught.
class ChildModel2(ParentModel):
@validator('names')
def check_names_not_empty(cls, v):
for name in v:
assert name != '', 'Empty strings are not allowed.'
return v
try:
child = ChildModel2(names=['Alice', 'Bob', 'Eve', ''])
except ValidationError as e:
print(e)
"""
1 validation error for ChildModel2
names
Empty strings are not allowed. (type=assertion_error)
"""
关键字参数 always 将导致始终验证,出于性能原因,默认情况下,当未提供值时,不会为字段调用验证器。然而,在某些情况下,始终调用验证器可能很有用或需要,例如设置动态默认值。
allow_reuse 可以在多个字段/模型上使用相同的验证器
from pydantic import BaseModel, validator
def normalize(name: str) -> str:
return ' '.join((word.capitalize()) for word in name.split(' '))
class Producer(BaseModel):
name: str
# validators
_normalize_name = validator('name', allow_reuse=True)(normalize)
class Consumer(BaseModel):
name: str
# validators
_normalize_name = validator('name', allow_reuse=True)(normalize)
六、配置
如果您创建一个继承自BaseSettings的模型,模型初始化程序将尝试通过从环境中读取,来确定未作为关键字参数传递的任何字段的值。(如果未设置匹配的环境变量,则仍将使用默认值。)
这使得很容易:
创建明确定义、类型提示的应用程序配置类
自动从环境变量中读取对配置的修改
在需要的地方手动覆盖初始化程序中的特定设置(例如在单元测试中)
from typing import Set
from pydantic import (
BaseModel,
BaseSettings,
PyObject,
RedisDsn,
PostgresDsn,
Field,
)
class SubModel(BaseModel):
foo = 'bar'
apple = 1
class Settings(BaseSettings):
auth_key: str
api_key: str = Field(..., env='my_api_key')
redis_dsn: RedisDsn = 'redis://user:pass@localhost:6379/1'
pg_dsn: PostgresDsn = 'postgres://user:pass@localhost:5432/foobar'
special_function: PyObject = 'math.cos'
# to override domains:
# export my_prefix_domains='["foo.com", "bar.com"]'
domains: Set[str] = set()
# to override more_settings:
# export my_prefix_more_settings='{"foo": "x", "apple": 1}'
more_settings: SubModel = SubModel()
class Config:
env_prefix = 'my_prefix_' # defaults to no prefix, i.e. ""
fields = {
'auth_key': {
'env': 'my_auth_key',
},
'redis_dsn': {
'env': ['service_redis_dsn', 'redis_url']
}
}
print(Settings().dict())
"""
{
'auth_key': 'xxx',
'api_key': 'xxx',
'redis_dsn': RedisDsn('redis://user:pass@localhost:6379/1',
scheme='redis', user='user', password='pass', host='localhost',
host_type='int_domain', port='6379', path='/1'),
'pg_dsn': PostgresDsn('postgres://user:pass@localhost:5432/foobar',
scheme='postgres', user='user', password='pass', host='localhost',
host_type='int_domain', port='5432', path='/foobar'),
'special_function': <built-in function cos>,
'domains': set(),
'more_settings': {'foo': 'bar', 'apple': 1},
}
"""
支持 Dotenv 文件设置变量,pydantic 有两种方式加载它:
class Settings(BaseSettings):
...
class Config:
env_file = '.env'
env_file_encoding = 'utf-8'
或者
settings=Settings(_env_file='prod.env',_env_file_encoding='utf-8')
即使使用 dotenv 文件,pydantic 仍会读取环境变量,环境变量将始终优先于从 dotenv 文件加载的值。
pydantic 支持设置敏感信息文件,同样有2种方式加载:
class Settings(BaseSettings):
...
database_password: str
class Config:
secrets_dir = '/var/run'
或者:
settings = Settings(_secrets_dir='/var/run')
即使使用 secrets 目录,pydantic仍会从 dotenv 文件或环境中读取环境变量,dotenv 文件和环境变量将始终优先于从 secrets 目录加载的值。
七、与 mypy 一起使用
Pydantic 附带了一个 mypy 插件,向 mypy 添加了许多重要的特定于 pydantic 的功能,以提高其对代码进行类型检查的能力。
例如以下脚本:
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, NoneStr
class Model(BaseModel):
age: int
first_name = 'John'
last_name: NoneStr = None
signup_ts: Optional[datetime] = None
list_of_ints: List[int]
m = Model(age=42, list_of_ints=[1, '2', b'3'])
print(m.middle_name) # not a model field!
Model() # will raise a validation error for age and list_of_ints
在没有任何特殊配置的情况下,mypy 会捕获其中一个错误:
13: error: "Model" has no attribute "middle_name"
启用插件后,它会同时捕获:
13: error: "Model" has no attribute "middle_name"
16: error: Missing named argument "age" for "Model"
16: error: Missing named argument "list_of_ints" for "Model"
要启用该插件,只需添加pydantic.mypy到mypy 配置文件中的插件列表:
[mypy]
plugins = pydantic.mypy
要更改插件设置的值,请在 mypy 配置文件中创建一个名为 的部分[pydantic-mypy],并为要覆盖的设置添加键值对:
[mypy]
plugins = pydantic.mypy
follow_imports = silent
warn_redundant_casts = True
warn_unused_ignores = True
disallow_any_generics = True
check_untyped_defs = True
no_implicit_reexport = True
# for strict mypy: (this is the tricky one :-))
disallow_untyped_defs = True
[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True
warn_untyped_fields = True
来源:https://juejin.cn/post/7079027549896081421


猜你喜欢
- MySQL中可以使用rename table这个SQL语句来修改表名。rename table这个SQL语句来修改表名的基本语法是:RENA
- 本文为大家分享了python数据分析数据标准化及离散化的具体内容,供大家参考,具体内容如下标准化1、离差标准化是对原始数据的线性变换,使结果
- 美餐每天发一个用Excel汇总的就餐数据,我们把它导入到数据库后,行政办公服务用它和公司内的就餐数据进行比对查重。初始实现是单线程,和imp
- 前言因为工作中经常用到这些方法,所有便把这些方法进行了总结。JavaScript 1. type 类型判断isString (o) { //
- python如何修改索引和行列修改索引修改索引之前是自动生成的索引:使用set_index('以xx字段为索引',inpla
- 1.Http连接基础Http协议承载了互联网上的主要流量,然而说到传输,还要回归到最基本的网络分层模型TCP/IP。TCP/IP是全球计算机
- pycharm安装cv2模块安装失败和无法使用的解决步骤一我们先到这个网址:cv2下载地址去下载与自己python版本号和电脑位数对应的op
- 前言最开始想尝试在windows下面安装python3.6,虽然python安装成功,但在安装Cryto模块用pip3 install py
- 先给大家巩固tempdb的基础知识简介:tempdb是SQLServer的系统数据库一直都是SQLServer的重要组成部分,用来存储临时对
- <html> <head> <meta http-equiv="Content-Langu
- 前言上篇文章记录了2种分割验证码的方法,此外还有一种叫做”滴水算法”(Drop Fall Algorithm)的方法,但本人智商原因看这个算
- 故障表现一方面 :在阿里云控制台云数据库PolarDB对应的集群管理页面上,在诊断与优化模块里面的一键诊断会话管理中,发现某条update
- 代码如下:SELECT * FROM Orders WHERE OrderGUID IN('BC71D821-9E25-
- Ansible Inventory 介绍Ansible Inventory 是包含静态 Inventory 和动态 Invent
- 什么是pyQt5首先我们来说说什么是pyQt5。搞过GUI界面设计的童鞋一定都了解过微软的MFC。当然了,个人感觉MFC不是一个非常优雅的U
- 概述:本文将通过组织自己的训练数据,使用Pytorch深度学习框架来训练自己的模型,最终实现自己的图像分类!本篇文章以识别阳台为例子,进行讲
- 一、读写文件在 Python 中,我们可以使用 open() 函数打开文件,with 语句可以自动关闭文件。读取文件内容:with open
- # -*- coding: utf-8 -*-import random#数字类class NumberItem: &
- Python httpx 运行过程中无限阻塞requests 模块只支持 http1,在遇到 http2 的数据接口的时候(某乎的搜索接口)
- 关于tensor.repeat()的使用考虑到很多人在学习这个函数,我想在这里提 一个建议:强烈推荐 使用 einops 模块中的 repe