pydantic 是 Python 中使用最广泛的数据验证库
介绍
- pydantic(“Py” and “pedantic”) 速度快、可扩展,可与 linters/IDE/brain 完美配合
- 在纯粹、规范的 Python 3.8+ 中定义数据;用 pydantic 解析和验证
- 在代码运行时强制执行类型提示,并在数据校验无效时提供友好的错误提示
安装
# 2.9.1
pip install pydantic
使用
- pydantic 中定义的对象需要继承
pydantic.BaseModel
类
简单使用
from pydantic import BaseModel
class User(BaseModel):
id: int # 必填
name: str = "xiexianbin" # 选填,str 类型,默认 xiexianbin
# 示例化
>>> user = User(id='123')
>>> print(user)
id=123 name='xiexianbin'
>>> print(user.__fields_set__ == {'id'})
True
>>> print(user.__fields_set__ == {'name'})
False
>>> print(user.__fields_set__ == {'id', 'name'})
False
# 输出类型
>>> print(user.dict())
{'id': 123, 'name': 'xiexianbin'}
>>> print(dict(user))
{'id': 123, 'name': 'xiexianbin'}
# 赋值
>>> user.id = 321
>>> print(user.dict())
{'id': 321, 'name': 'xiexianbin'}
>>> print(user.json(), type(user.json()))
{"id":321,"name":"xiexianbin"} <class 'str'>
>>> print(user.schema(), type(user.schema()))
{'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'name': {'default': 'xiexianbin', 'title': 'Name', 'type': 'string'}}, 'required': ['id'], 'title': 'User', 'type': 'object'} <class 'dict'>
>>> print(user.schema_json(), type(user.schema_json()))
{"properties": {"id": {"title": "Id", "type": "integer"}, "name": {"default": "xiexianbin", "title": "Name", "type": "string"}}, "required": ["id"], "title": "User", "type": "object"} <class 'str'>
# 浅拷贝
>>> user2 = user.copy()
>>> print(user2)
id=321 name='xiexianbin'
from pydantic import BaseModel, Field, EmailStr
from typing import Dict, List, Sequence, Set, Tuple, Union
class Demo(BaseModel):
a: int # 整型
b: float # 浮点型
c: str # 字符串
d: bool # 布尔型
e: List[int] # 整型列表
f: Dict[str, int] # 字典型,key 为 str,value 为 int
g: Set[int] # 集合
h: Tuple[str, int] # 元组
time: Union[int, str] # 多种数据类型
password: str = Field(alias = "key") # 别名,Field 来定义字段的描述、默认值、取值范围等
price: float = Field(..., description="商品价格", gt=1, lt=1000)
email: EmailStr
嵌套类型
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
class User(BaseModel):
id: int # 必填字段
name: str = "xiexianbin" # 有默认值,选填字段
signup_ts: Optional[datetime] = None
friends: List[int] = [] # int 类型列表,或可以直接转成 int 类型的列表
# 实例化
user = User(id="1", signup_ts="2006-06-13 04:05")
print(user.dict())
# output
{'id': 1, 'name': 'xiexianbin', 'signup_ts': datetime.datetime(2006, 6, 13, 4, 5), 'friends': []}
# dict 解包传参
data = {
"id": "2",
"name": "xianbin",
"friends": [1, 2, 3]
}
user = User(**data)
print(user.dict())
# output
{'id': 2, 'name': 'xianbin', 'signup_ts': None, 'friends': [1, 2, 3]}
from typing import List
from pydantic import BaseModel
class Foo(BaseModel):
count: int
size: float = None
class Bar(BaseModel):
name: str = 'x'
address: str = 'y'
class Spam(BaseModel):
foo: Foo
bars: List[Bar]
f = Foo(count=2)
b = Bar()
s = Spam(foo=f, bars=[b])
print(s.dict())
# output
{'foo': {'count': 2, 'size': None}, 'bars': [{'name': 'x', 'address': 'y'}]}
错误处理
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name: str
try:
p = User(id="xx", name="xiexianbin") # id 是个 int 类型,这里转换失败会报错
except ValidationError as e:
print(e.errors())
print(e.json())
print(str(e))
# output
[{'type': 'int_parsing', 'loc': ('id',), 'msg': 'Input should be a valid integer, unable to parse string as an integer', 'input': 'xx', 'url': 'https://errors.pydantic.dev/2.9/v/int_parsing'}]
[{"type":"int_parsing","loc":["id"],"msg":"Input should be a valid integer, unable to parse string as an integer","input":"xx","url":"https://errors.pydantic.dev/2.9/v/int_parsing"}]
1 validation error for User
id
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='xx', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/int_parsing
验证示例
from pydantic import conint, BaseModel, ValidationError
class Location(BaseModel):
lat: float = 0.1
lng: float = 10.1
class Model(BaseModel):
is_required: float
gt_int: conint(gt=18)
list_of_ints: List[int] = None
a_float: float = None
recursive_model: Location = None
data = dict(
list_of_ints=['1', 2, 'bad'],
a_float='not a float',
recursive_model={'lat': 4.2, 'lng': 'New York'},
gt_int=17
)
try:
Model(**data)
except ValidationError as e:
print(e.json(indent=4))
[
{
"type": "missing",
"loc": [
"is_required"
],
"msg": "Field required",
"input": {
"list_of_ints": [
"1",
2,
"bad"
],
"a_float": "not a float",
"recursive_model": {
"lat": 4.2,
"lng": "New York"
},
"gt_int": 17
},
"url": "https://errors.pydantic.dev/2.9/v/missing"
},
{
"type": "greater_than",
"loc": [
"gt_int"
],
"msg": "Input should be greater than 18",
"input": 17,
"ctx": {
"gt": 18
},
"url": "https://errors.pydantic.dev/2.9/v/greater_than"
},
{
"type": "int_parsing",
"loc": [
"list_of_ints",
2
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "bad",
"url": "https://errors.pydantic.dev/2.9/v/int_parsing"
},
{
"type": "float_parsing",
"loc": [
"a_float"
],
"msg": "Input should be a valid number, unable to parse string as a number",
"input": "not a float",
"url": "https://errors.pydantic.dev/2.9/v/float_parsing"
},
{
"type": "float_parsing",
"loc": [
"recursive_model",
"lng"
],
"msg": "Input should be a valid number, unable to parse string as a number",
"input": "New York",
"url": "https://errors.pydantic.dev/2.9/v/float_parsing"
}
]
from pydantic import BaseModel, ValidationError
class Password(BaseModel):
password: str
class Config:
str_min_length = 6 # 不少于6
str_max_length = 20 # 不大于20
try:
Password(password='abcd')
except ValidationError as e:
print(e.json())
# [{"type":"string_too_short","loc":["password"],"msg":"String should have at least 6 characters","input":"abcd","ctx":{"min_length":6},"url":"https://errors.pydantic.dev/2.9/v/string_too_short"}]
try:
Password(password='abcdefghicklmnopqrstuvwxyz')
except ValidationError as e:
print(e.json())
# [{"type":"string_too_long","loc":["password"],"msg":"String should have at most 20 characters","input":"abcdefghicklmnopqrstuvwxyz","ctx":{"max_length":20},"url":"https://errors.pydantic.dev/2.9/v/string_too_long"}]
自定义错误
from pydantic import BaseModel, ValidationError, field_validator
class Model(BaseModel):
foo: str
@field_validator('foo')
def name_must_eq_bar(cls, v):
if v != 'bar':
raise ValueError('value must be bar')
return v
try:
Model(foo="foo")
except ValidationError as e:
print(e.json())
[{"type":"value_error","loc":["foo"],"msg":"Value error, value must be bar","input":"foo","ctx":{"error":"value must be bar"},"url":"https://errors.pydantic.dev/2.9/v/value_error"}]