首页 > 编程语言 >python中pydantic库

python中pydantic库

时间:2022-09-27 13:14:01浏览次数:66  
标签:name 验证 python BaseModel print import 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标准类型(例如ListTupleDict)和 验证器允许清晰、轻松地定义、验证和解析复杂的数据模式。

  • 可扩展

    pydantic允许定义自定义数据类型,或者您可以使用validator装饰器装饰的模型上的方法扩展验证。

  • 数据类集成

    以及BaseModelpydantic提供了一个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/

标签:name,验证,python,BaseModel,print,import,pydantic
From: https://www.cnblogs.com/liuzhongkun/p/16734222.html

相关文章

  • Python学习笔记3
    problem1方程\(ax2+bx+c=0\),输入\(a,b,c\)的值,根据\(a,b,c\)的值判断不同的输出情况情况,判断并输出结果“有两个根\(x1=?\),\(x2=?\)”;“有一个根\(x=?\)”;“没有根”......
  • IPython的%魔术命令
    %magic显示所有魔术命令%hist 显示IPython命令的输入历史%pdb 异常发生后自动进入调试器%reset 删除当前命名空间中的全部变量或名称%who 显示IPython当前命......
  • 女同桌找我要表情包,还好我会Python,分分钟给她下载几十个G...
    emmm~起因呢,这昨晚女同桌跟我说电脑有点卡,喊我去宿舍给她装个新系统,装系统就装系统吧,结果又说新系统表情包都没保存~我当时就有点生气,真当我是万能的呢?于是我直接就用Py......
  • python使用paramiko实现ssh定时执行命令
    原文https://www.cnblogs.com/Rosaany/p/16093521.html#!/usr/bin/envpython3#-*-coding:utf-8-*-#@Author:Rosaanyimportfunctoolsfromparamiko.ssh_exce......
  • Python 安装
    进入Python官网选择安装包根据自己需要选择安装包,这里我选用的是Python3.10.7的完整安装包Windowsinstaller(64-bit),注意:embeddable:表示绿色免安装版本,可......
  • python时间加减
      运行结果:  ......
  • python 实现发送邮件功能
    一、前言   在开始正题之前,我们先理一下常见的电子邮件协议: SMTP、POP、IMAP都遵循TCP/IP协议规范。至于Exchange是邮件服务器,不是收邮件和发邮件的协议,不要混淆概......
  • Python多行字符串
    Sometimeswehaveaverylongstringandwewanttowriteitintomultiplelinesforbettercodereadability.Pythonprovidesvariouswaystocreatemultiline......
  • python中利用smtplib发送邮件的3中方式 普通/ssl/tls
    #!/usr/bin/python#coding:utf-8importsmtplibfromemail.MIMETextimportMIMETextfromemail.Utilsimportformatdatefromemail.HeaderimportHe......
  • Linux升级python至3.x
    前言云服务器一般都用Linux系统,现在云服务器的Linux一般自带python,只是版本是2.x,比较老的那种。大部分人用的python应该都是3.x版本的,这时候你在本地编写的python文件拿到......