Pydantic: Python 验证库

发布时间: 更新时间: 总字数:1028 阅读时间:3m 作者: IP上海 分享 网址

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))
  • output
[
    {
        "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())
  • output
[{"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"}]

参考

  1. https://docs.pydantic.dev/
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数