之前在学习 FastAPI的时候,FastAPI 构建 API 高性能的 web 框架(一) 看到pydantic
使用场景非常多,也单独来看看这个模块。
pydantic的官方文档:https://docs.pydantic.dev/latest/
Usage界面:https://docs.pydantic.dev/latest/usage/models/
pydantic 在运行时强制执行类型提示,并在数据无效时提供友好的错误。
它具有如下优点:
同时可以检查的python格式包括:
BaseModel的基本属性包括:
先来个比较简单的版本:
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str = 'John Doe' # name是字符型,同时设定了一个默认值
定义了一个User模型,继承自BaseModel,有2个字段,id是一个整数并且是必需的,name是一个带有默认值的字符串并且不是必需的
实例化使用:
# 情况一:因为定义了User类中id是数字,所以这里实例化后,如果可以变成数字的,直接转化
user = User(id='123')
>>> '{"id": 123, "name": "Jane Doe"}'
# 情况二:定义id为整数,且不可以转化为整数,则会如上报错
user = User(id='123a')
>>> ValidationError: 1 validation error for User
id
value is not a valid integer (type=type_error.integer)
# 情况三:定义id为整数,此时是float格式,也会报错
user = User(id='123.2')
>>> ValidationError: 1 validation error for User
id
value is not a valid integer (type=type_error.integer)
再来一段复杂点的案例:
from pydantic import ValidationError
# 这里规定了id必须是int类型
class User(BaseModel):
id: int
name: str = 'John Doe'
signup_ts: datetime | None # 字符型或者None都是可以的
tastes: dict[str, PositiveInt] # 字典型,规定key是字符型,value一定是正整数型
address: Optional[Address] # Optional可选是否填写
bool_value: bool
# 正负取值,print(BooleanModel(bool_value='False'))
# 然而这里id给了个str类型
external_data = {'id': 'not an int', 'tastes': {}}
try:
User(**external_data)
except ValidationError as e:
print(e.errors())
关于List / Tuple这类的限定:
参考:文档
from typing import Deque, List, Optional, Tuple
from pydantic import BaseModel
class Model(BaseModel):
simple_list: Optional[list] = None
list_of_ints: Optional[List[int]] = None
simple_tuple: Optional[tuple] = None
tuple_of_different_types: Optional[Tuple[int, float, bool]] = None
deque: Optional[Deque[int]] = None
print(Model(simple_list=['1', '2', '3']).simple_list)
#> ['1', '2', '3']
print(Model(list_of_ints=['1', '2', '3']).list_of_ints)
#> [1, 2, 3]
print(Model(simple_tuple=[1, 2, 3, 4]).simple_tuple)
#> (1, 2, 3, 4)
print(Model(tuple_of_different_types=[3, 2, 1]).tuple_of_different_types)
#> (3, 2.0, True)
print(Model(deque=[1, 2, 3]).deque)
#> deque([1, 2, 3])
可以嵌套比较复杂的结构,同时都是可选的,同时嵌套结构可以进行定义
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类型,以防止强制兼容类型
再来看一个例子:
from typing import Annotated, Dict, List, Literal, Tuple
from annotated_types import Gt
from pydantic import BaseModel
class Fruit(BaseModel):
name: str
color: Literal['red', 'green']
# 规定color是字符型,同时只能在red / green两个文本中进行选择
weight: Annotated[float, Gt(0)]
bazam: Dict[str, List[Tuple[int, bool, float]]]
# 定义bazam为字符型,key为str型,value为List型,这里的嵌套结构比较多
print(
Fruit(
name='Apple',
color='red',
weight=4.2,
bazam={'foobar': [(1, True, 0.1)]},
)
)
from pydantic import BaseModel, ValidationError, validator
class UserModel(BaseModel):
# 先规定UserModel必要的几个参数
name: str
username: str
password1: str
password2: str
# 然后校验不同参数,validator()第一个参数name
@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)
再来举一个验证字符内容(验证输入的手机号码)的例子:
import re
from pydantic import BaseModel, validator, ValidationError
from typing import Optional
class Address(BaseModel):
street: str
number: int
zipcode: str
class Person(BaseModel):
first_name: str
last_name: str
cell_phone_number: str
address: Optional[Address]
@validator("cell_phone_number")
def validate_cell_phone_number(cls, v):
match = re.match(r"^135\d{8}$", v)
if len(v) != 11:
raise ValueError("cell phone number must be 11 digits")
elif match is None:
raise ValueError("cell phone number must start with 135")
return v
这里验证手机号,可以看到re.match(r"^135\d{8}$", v)
代表着,检查这串手机号,是否是135
开头,以及后面是否是8位数。
可以一个数列连续验证多次,同时对数列进行一些操作,比如加减乘除
from typing import Any, List
from typing_extensions import Annotated
from pydantic import BaseModel, ValidationError
from pydantic.functional_validators import AfterValidator
def check_squares(v: int) -> int:
assert v**0.5 % 1 == 0, f'{v} is not a square number'
return v
def double(v: Any) -> Any:
return v * 2
# 核心在Annotated这个函数
MyNumber = Annotated[int, AfterValidator(double), AfterValidator(check_squares)]
class DemoModel(BaseModel):
number: List[MyNumber]
print(DemoModel(number=[2, 8]))
#> number=[4, 16]
try:
DemoModel(number=[2, 4])
except ValidationError as e:
print(e)
"""
1 validation error for DemoModel
number.1
Assertion failed, 8 is not a square number
assert ((8 ** 0.5) % 1) == 0 [type=assertion_error, input_value=4, input_type=int]
"""
核心解读一下这句话:
Annotated[int, AfterValidator(double), AfterValidator(check_squares)]
解读一下,
AfterValidator(double)
此时double
其实是一个函数,将[2,4]
变成[4,16]
,AfterValidator(check_squares)
,检查[4,16]
是否都是平方数,4是2的平方,16是4的平方参考文献