python中pydantic库
pydantic库详解
一、 概述
1、 简介
该库的官方文档位置为:https://pydantic-docs.helpmanual.io/
不知道大家是否非常羡慕C语言等在进行函数传参时,可以指定数据类型来传参呢?
我之前有一篇讲过使用typing
来指定数据类型,但是其仅仅是能指定数据类型,只能做一个提醒的作用,那么我们如何来结合typing
模块,来写一个可以像Java等语言的指定参数类型呢?这里我推荐pydantic
库。
首先,在学这个库之前,我们需要去回顾一下typing
库的使用方法:https://blog.csdn.net/qq_62789540/article/details/124790174
然后,我们来解释一下我们即将要学的库:
其使用 Python 类型注释的数据验证和设置管理。
pydantic在运行时强制执行类型提示,并在数据无效时提供用户友好的错误。
定义数据应该如何在纯的、规范的 Python 中保存;用pydantic验证它。
2、 优势
所以pydantic使用了一些很酷的新语言特性,但我为什么要实际去使用它呢?
-
与您的 IDE/linter/brain 配合得很好
无需学习新的模式定义微语言。如果你知道如何使用 Python 类型提示,你就会知道如何使用pydantic。数据结构只是您使用类型注释定义的类的实例,因此自动完成、linting、mypy、IDE(尤其是PyCharm)和您的直觉都应该与您的验证数据正常工作。
-
两用
pydantic 的 BaseSettings类允许在“验证此请求数据”上下文和“加载我的系统设置”上下文中使用pydantic 。主要区别在于系统设置可以从环境变量中读取,并且通常需要更复杂的对象,例如 DSN 和 Python 对象。
-
快速地
pydantic一直非常重视性能,大多数库都是用 cython 编译的,加速了约 50%,它通常与大多数类似库一样快或更快。
-
验证复杂结构
使用递归pydantic模型、
typing
的 标准类型(例如List
、Tuple
等Dict
)和 验证器允许清晰、轻松地定义、验证和解析复杂的数据模式。 -
可扩展
-
数据类集成
以及
BaseModel
,pydantic提供了一个dataclass
装饰器,它创建(几乎)带有输入数据解析和验证的普通 Python 数据类。
3、 环境配置
安装这个库的方法非常简单:
pip install pydantic
安装配置的扩展功能:
pip install pydantic[email] # 邮箱验证支持 # or pip install pydantic[dotenv] # dotenv文件支持 # or just pip install pydantic[email, dotenv] # 同时安装
如果想要通过其他方式安装,可以参考官方文档
二、 Model
1、 模型属性
在pydantic中定义对象的主要方法是通过模型(模型只是继承自 的类BaseModel
)。
您可以将模型视为类似于严格类型语言中的类型,或者视为 API 中单个端点的要求。
不受信任的数据可以传递给模型,并且在解析和验证之后,pydantic保证生成的模型实例的字段将符合模型上定义的字段类型。
属性 | 描述 |
---|---|
dict() |
返回模型字段和值的字典 |
json() |
返回一个JSON字符串表示dict() |
copy() |
返回模型的副本,浅拷贝 |
parse_obj() |
如果对象不是字典,则用于将任何对象加载到具有错误处理的模型中的实用程序 |
parse_raw() |
用于加载多种格式字符串的实用程序 |
from_orm() |
将数据从任意类加载到模型中 |
schema() |
返回将模型表示为JSON Schema的字典 |
schema_json() |
返回schema() 的JSON字符串表示形式 |
construct() |
无需运行验证即可创建模型的类方法 |
__fields_set__ |
初始化模型实例时设置字段名称集 |
__fields__ |
模型字段的字典 |
__config__ |
模型的配置类 |
2、 基本使用
from pydantic import BaseModel class Foo(BaseModel): count: int size: float | None = None class Bar(BaseModel): apple = 'x' banana = 'y' class Spam(BaseModel): foo: Foo bars: list[Bar] m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}]) print(m) #> foo=Foo(count=4, size=None) bars=[Bar(apple='x1', banana='y'), #> Bar(apple='x2', banana='y')] print(m.dict()) """ { 'foo': {'count': 4, 'size': None}, 'bars': [ {'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}, ], } """
3、数据导入
3.1 orm
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_3_9.CompanyOrm object at 0x7fb20cc17790> co_model = CompanyModel.from_orm(co_orm) print(co_model) #> id=123 public_key='foobar' name='Testing' domains=['example.com', #> 'foobar.com']
3.2 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'
3.3 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)
4、 数据导出
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" ] } """
三、 验证器
1、 类内添加
能给它增加 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) """
2、 重用验证器
有时,您会希望在多个字段/模型上使用相同的验证器(例如,规范化某些输入数据)。方法是编写一个单独的函数,然后从多个装饰器中调用它。显然,这需要大量重复和样板代码。为了避免这种情况,在v1.2allow_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) jane_doe = Producer(name='JaNe DOE') john_doe = Consumer(name='joHN dOe') assert jane_doe.name == 'Jane Doe' assert john_doe.name == 'John Doe'
3、 根验证器
from pydantic import BaseModel, ValidationError, root_validator class UserModel(BaseModel): username: str password1: str password2: str @root_validator(pre=True) def check_card_number_omitted(cls, values): assert 'card_number' not in values, 'card_number should not be included' return values @root_validator def check_passwords_match(cls, values): pw1, pw2 = values.get('password1'), values.get('password2') if pw1 is not None and pw2 is not None and pw1 != pw2: raise ValueError('passwords do not match') return values print(UserModel(username='scolvin', password1='zxcvbn', password2='zxcvbn')) #> username='scolvin' password1='zxcvbn' password2='zxcvbn' try: UserModel(username='scolvin', password1='zxcvbn', password2='zxcvbn2') except ValidationError as e: print(e) """ 1 validation error for UserModel __root__ passwords do not match (type=value_error) """ try: UserModel( username='scolvin', password1='zxcvbn', password2='zxcvbn', card_number='1234', ) except ValidationError as e: print(e) """ 1 validation error for UserModel __root__ card_number should not be included (type=assertion_error) """
与字段验证器一样,根验证器可以具有pre=True
,在这种情况下,在字段验证发生之前调用它们(并提供原始输入数据),或者pre=False
(默认),在这种情况下,在字段验证之后调用它们。
pre=True
如果根验证器引发错误,则不会发生字段验证。与字段验证器一样,pre=False
即使先前的验证器失败,也会默认调用“post”(即)根验证器;skip_on_failure=True
可以通过将关键字参数设置为验证器来更改此行为。该values
参数将是一个字典,其中包含通过字段验证的值和适用的字段默认值。
4、 验证装饰器
validate_arguments
装饰器允许在调用函数之前使用函数的注释解析和验证传递给函数的参数。在引擎盖下,它使用相同的模型创建和初始化方法;它提供了一种非常简单的方法,可以用最少的样板对代码应用验证。
import os from pathlib import Path from typing import Pattern, Optional from pydantic import validate_arguments, DirectoryPath @validate_arguments def find_file(path: DirectoryPath, regex: Pattern, max=None) -> Optional[Path]: for i, f in enumerate(path.glob('**/*')): if max and i > max: return if f.is_file() and regex.fullmatch(str(f.relative_to(path))): return f # note: this_dir is a string here this_dir = os.path.dirname(__file__) print(find_file(this_dir, '^validation.*')) #> /home/runner/work/pydantic/pydantic/docs/examples/validation_decorator_async. #> py print(find_file(this_dir, '^foobar.*', max=3)) #> None
几点注意事项:
- 尽管它们作为字符串传递,
path
并由装饰器分别regex
转换为对象和正则表达式Path
max
没有类型注释,所以会被装饰器认为是Any
这些是常用的方法,想要了解更详细的内容,可以去阅读官方文档:https://pydantic-docs.helpmanual.io/
本文来自博客园,作者:Kenny_LZK,转载请注明原文链接:https://www.cnblogs.com/liuzhongkun/p/16734222.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?