FastAPI-5:Pydantic、类型提示和模型预览

news/2025/1/20 20:01:26/文章来源:https://www.cnblogs.com/testing-/p/18236466

5 Pydantic、类型提示和模型

FastAPI主要基于Pydantic。它使用模型(Python对象类)来定义数据结构。这些模型在FastAPI应用程序中被大量使用,是编写大型应用程序时的真正优势。

5.1 类型提示

在许多计算机语言中,变量直接指向内存中的值。这就要求程序员声明它的类型,以便确定值的大小和位数。在Python中,变量只是与对象相关联的名称,而对象才有类型。

变量通常与同一个对象相关联。如果我们将类型提示与变量关联起来,就可以避免一些编程错误。因此,Python 在语言的标准类型模块中加入了类型提示。Python解释器会忽略类型提示语法,运行程序时就像没有类型提示一样。那有什么意义呢?

您可能在一行中将一个变量视为字符串,但后来却忘记了,并将其赋值给一个不同类型的对象。虽然其他语言的编译器会提示,但 Python不会。标准 Python解释器会捕获正常的语法错误和运行时异常,但不会捕获变量的混合类型。像mypy这样的辅助工具会关注类型提示,并对任何不匹配发出警告。

Python开发人员也可以使用这些提示,他们可以编写比类型错误检查更多的工具。下面几节将介绍Pydantic软件包是如何开发的,以满足一些并不明显的需求。稍后,您将看到它与FastAPI的集成如何使许多网络开发问题变得更容易处理。

变量类型提示可能只包括类型:name: type

或者用一个值初始化变量:name: type = value
类型可以是标准的Python简单类型,如int或str,也可以是复杂类型,如tuple、list或dict:name: type = value

在Python 3.9之前,需要从typing模块导入这些标准类型名的大写版本:

from typing import Str
thing: Str = "yeti"

下面是一些带有初始化的示例:

physics_magic_number: float = 1.0/137.03599913
hp_lovecraft_noun: str = "ichor"
exploding_sheep: tuple = "sis", "boom", bah!"
responses: dict = {"Marco": "Polo", "answer": 42}

还可以指定子类型:name: dict[keytype, valtype] = {key1: val1, key2: val2}
最常见的子类如下:

  • Any:任意类型
  • Union:任何指定类型,如 Union[str,int]。

在Python 3.10及更高版本中,可以用type1 | type2 代替 Union[type1,type2]。

Python dict的 Pydantic定义示例如下:

from typing import Any
responses: dict[str, Any] = {"Marco": "Polo", "answer": 42}

或者更具体一点

from typing import Any
responses: dict[str, Any] = {"Marco": "Polo", "answer": 42}

或 (Python 3.10 及更高版本):

responses: dict[str, str | int] = {"Marco": "Polo", "answer": 42}

请注意,类型提示的变量行是合法的Python,而裸变量行则不是:

$ python
...
>>> thing0
Traceback (most recent call last):File "<stdin>", line 1, in <module>
NameError: name thing0 is not defined
>>> thing0: str

此外,Python解释器不会捕获不正确的类型使用:

$ python
...
>>> thing1: str = "yeti"
>>> thing1 = 47

但它们会被mypy捕获。如果还没有,运行 pip install mypy。将前面两行保存到名为 stuff.py,1 的文件中,然后试试下面的方法:

$ mypy stuff.py
stuff.py:2: error: Incompatible types in assignment
(expression has type "int", variable has type "str")
Found 1 error in 1 file (checked 1 source file)

函数返回类型提示使用了箭头而不是冒号:function(args) -> type:

下面是一个函数返回的 Pydantic 示例:

def get_thing() -> str:return "yeti"

5.2 数据分组

通常,我们需要将一组相关的变量放在一起,而不是传递大量的单个变量。如何将多个变量整合为一组并保持类型提示呢?在本书的其他章节中,我们将使用密码生物(想象中的生物)和寻找它们的探险家(也是想象中的)的例子。我们最初的密码生物定义将只包含以下字符串变量:

  • name:关键字
  • country:两字符 ISO 国家代码(3166-1 alpha 2)或 * = 全部
  • area 可选;美国州或其他国家分区
  • description:自由格式
  • aka:又称

而探险者将拥有以下内容:

  • name:关键字
  • country:两字符 ISO 国家代码(3166-1 alpha 2)或 * = 全部
  • area 可选;美国州或其他国家分区
  • description:自由格式

这里列出了Python数据分组结构(除了基本的 int、字符串之类):

  • 元组:不可变的对象序列
  • 列表:可变的对象序列
  • 集合:可变的不同对象
  • 字典:可变的键值对象对(键必须是不可变的类型)
# 使用元组
>>> tuple_thing = ("yeti", "CN", "Himalayas","Hirsute Himalayan", "Abominable Snowman")
>>> print("Name is", tuple_thing[0])
Name is yeti# 使用列表
>>> list_thing = ["yeti", "CN", "Himalayas","Hirsute Himalayan", "Abominable Snowman"]
>>> print("Name is", list_thing[0])
Name is yeti# 使用元组和命名偏移量
>>> NAME = 0
>>> COUNTRY = 1
>>> AREA = 2
>>> DESCRIPTION = 3
>>> AKA = 4
>>> tuple_thing = ("yeti", "CN", "Himalayas","Hirsute Himalayan", "Abominable Snowman")
>>> print("Name is", tuple_thing[NAME])
Name is yeti# 使用字典
>>> dict_thing = {"name": "yeti",
...     "country": "CN",
...     "area": "Himalayas",
...     "description": "Hirsute Himalayan",
...     "aka": "Abominable Snowman"}
>>> print("Name is", dict_thing["name"])
Name is yeti# 使用命名元组
>>> from collections import namedtuple
>>> CreatureNamedTuple = namedtuple("CreatureNamedTuple",
...     "name, country, area, description, aka")
>>> namedtuple_thing = CreatureNamedTuple("yeti",
...     "CN",
...     "Himalaya",
...     "Hirsute HImalayan",
...     "Abominable Snowman")
>>> print("Name is", namedtuple_thing[0])
Name is yeti
>>> print("Name is", namedtuple_thing.name)
Name is yeti# 标准类
>>> class CreatureClass():
...     def __init__(self,
...       name: str,
...       country: str,
...       area: str,
...       description: str,
...       aka: str):
...         self.name = name
...         self.country = country
...         self.area = area
...         self.description = description
...         self.aka = aka
...
>>> class_thing = CreatureClass(
...     "yeti",
...     "CN",
...     "Himalayas"
...     "Hirsute Himalayan",
...     "Abominable Snowman")
>>> print("Name is", class_thing.name)
Name is yeti# 数据类
>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class CreatureDataClass():
...     name: str
...     country: str
...     area: str
...     description: str
...     aka: str
...
>>> dataclass_thing = CreatureDataClass(
...     "yeti",
...     "CN",
...     "Himalayas"
...     "Hirsute Himalayan",
...     "Abominable Snowman")
>>> print("Name is", dataclass_thing.name)
Name is yeti

这对于保持变量的一致性来说已经很不错了。但我们还想要更多:

  • 可能的替代类型的联合
  • 缺失/可选值
  • 默认值
  • 数据验证
  • 与JSON等格式的序列化

5.3 替代方案

使用 Python 内置的数据结构(尤其是字典)很有诱惑力。但你不可避免地会发现字典有点过于 “松散”。自由是有代价的。你需要检查一切:

  • 键是否可选?
  • 如果键缺失,是否有默认值?
  • 键是否存在?
  • 如果存在,键值的类型是否正确?
  • 如果存在,值是否在正确的范围内或与模式匹配?

至少有三种解决方案可以满足这些要求中的至少一部分:Dataclasses、attrs(Dataclasses的超集)、Pydantic(集成到了FastAPI中)。

Pydantic 在验证方面非常突出,它与FastAPI的集成可以捕捉到许多潜在的数据错误。Pydantic依赖于继承(从 BaseModel 类继承),而其他两个软件则使用Python装饰器来定义对象。Pydantic 的另一大优点是它使用了标准的 Python 类型提示语法,而旧版库则在类型提示之前就自行推出了类型提示。

因此,我在本书中将使用 Pydantic,但如果你不使用 FastAPI,你也可能会发现这两种库中的任何一种都有用武之地。

Pydantic 提供了指定这些检查的任意组合的方法:

  • 必须与可选
  • 未指定但需要的默认值
  • 预期的数据类型
  • 值范围限制
  • 其他基于函数的检查(如果需要)
  • 序列化和反序列化

参考资料

  • 软件测试精品书籍文档下载持续更新 https://github.com/china-testing/python-testing-examples 请点赞,谢谢!
  • 本文涉及的python测试开发库 谢谢点赞! https://github.com/china-testing/python_cn_resouce
  • python精品书籍下载 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
  • Linux精品书籍下载 https://www.cnblogs.com/testing-/p/17438558.html

5.4简单示例

这个初始示例将使用三个文件:

  • model.py定义Pydantic 模型。
  • data.py 假数据源,定义了一个模型实例。
  • web.py 定义了返回假数据的FastAPI网络端点。

定义生物模型:model.py

from pydantic import BaseModelclass Creature(BaseModel):name: strcountry: strarea: strdescription: straka: strthing = Creature(name="yeti",country="CN",area="Himalayas",description="Hirsute Himalayan",aka="Abominable Snowman")print("Name is", thing.name)

Creature类继承自Pydantic的BaseModel。name、country、area、description和aka后面的 : str部分是类型提示,表明每个字符串都是Python字符串。

>>> thing = Creature(
...     name="yeti",
...     country="CN",
...     area="Himalayas"
...     description="Hirsute Himalayan",
...     aka="Abominable Snowman")
>>> print("Name is", thing.name)
Name is yeti

在 data.py 中定义假数据
从模型导入生物

from model import Creature_creatures: list[Creature] = [Creature(name="yeti",country="CN",area="Himalayas",description="Hirsute Himalayan",aka="Abominable Snowman"),Creature(name="sasquatch",country="US",area="*",description="Yeti's Cousin Eddie",aka="Bigfoot")
]def get_creatures() -> list[Creature]:return _creatures

这段代码导入了我们刚刚编写的 model.py。通过调用它的生物对象列表 _creatures,并提供 get_creatures() 函数来返回它们,它做了一点数据隐藏。

web.py:

from model import Creature
from fastapi import FastAPIapp = FastAPI()@app.get("/creature")
def get_all() -> list[Creature]:from data import get_creaturesreturn get_creatures()if __name__ == "__main__":import uvicornuvicorn.run("web:app", reload=True)

现在启动服务器。

$ python web.py
INFO:     Will watch for changes in these directories: ['D:\\code\\fastapi-main\\example']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [19124] using WatchFiles
INFO:     Started server process [22344]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

验证:

$ http http://localhost:8000/creature
HTTP/1.1 200 OK
content-length: 211
content-type: application/json
date: Sat, 08 Jun 2024 02:20:40 GMT
server: uvicorn[{"aka": "Abominable Snowman","area": "Himalayas","country": "CN","description": "Hirsute Himalayan","name": "yeti"},{"aka": "Bigfoot","area": "*","country": "US","description": "Yeti's Cousin Eddie","name": "sasquatch"}
]

5.5 验证类型

试着给一个或多个 “生物 ”字段分配一个错误类型的值。让我们使用独立测试来实现这一点(Pydantic 并不适用于任何网页代码;这是一个数据问题)。

from model import Creaturedragon = Creature(name="dragon",description=["incorrect", "string", "list"],country="*")

运行测试

$ python 5-14.py
Name is yeti
Traceback (most recent call last):File "D:\code\fastapi-main\example\5-14.py", line 3, in <module>dragon = Creature(File "C:\Users\xuron\AppData\Roaming\Python\Python310\site-packages\pydantic\main.py", line 176, in __init__self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 3 validation errors for Creature
areaField required [type=missing, input_value={'name': 'dragon', 'descr...'list'], 'country': '*'}, input_type=dict]For further information visit https://errors.pydantic.dev/2.7/v/missing
descriptionInput should be a valid string [type=string_type, input_value=['incorrect', 'string', 'list'], input_type=list]For further information visit https://errors.pydantic.dev/2.7/v/string_type
akaField required [type=missing, input_value={'name': 'dragon', 'descr...'list'], 'country': '*'}, input_type=dict]For further information visit https://errors.pydantic.dev/2.7/v/missing

5.6 验证值

即使值的类型符合 Creature 类中的说明,也可能需要通过更多检查。可以对值本身进行一些限制:

  • Integer (conint)或float:

    • gt: 大于
    • lt:小于
    • ge:大于或等于
    • le:小于或等于
    • multiple_of: 数值的整数倍
  • String (constr):

    • min_length:字符(非byte)的最小长度
    • max_length:最大字符长度
    • to_upper:转换为大写字母
    • to_lower:转为小写
    • regex:匹配Python正则表达式
  • 元组、列表或集合:

    • min_items:最小元素数
    • max_items:元素的最大数量

这些在模型的类型部分中指定。

实例:确保名称字段总是至少有两个字符长。否则,“”(空字符串)就是有效字符串。

from pydantic import BaseModel, constrclass Creature(BaseModel):name: constr(min_length=2)country: strarea: strdescription: straka: strbad_creature = Creature(name="!", description="it's a raccoon", area="your attic")

执行:

Traceback (most recent call last):File D:\ProgramData\anaconda3\lib\site-packages\spyder_kernels\py3compat.py:356 in compat_execexec(code, globals, locals)File d:\code\test5.py:10bad_creature = Creature(name="!", description="it's a raccoon", area="your attic")File ~\AppData\Roaming\Python\Python310\site-packages\pydantic\main.py:176 in __init__self.__pydantic_validator__.validate_python(data, self_instance=self)ValidationError: 3 validation errors for Creature
nameString should have at least 2 characters [type=string_too_short, input_value='!', input_type=str]For further information visit https://errors.pydantic.dev/2.7/v/string_too_short
countryField required [type=missing, input_value={'name': '!', 'descriptio...", 'area': 'your attic'}, input_type=dict]For further information visit https://errors.pydantic.dev/2.7/v/missing
akaField required [type=missing, input_value={'name': '!', 'descriptio...", 'area': 'your attic'}, input_type=dict]For further information visit https://errors.pydantic.dev/2.7/v/missing

下列使用了另一种方法,即Pydantic字段规范。

from pydantic import BaseModel, constrclass Creature(BaseModel):name: constr(min_length=2)country: strarea: strdescription: straka: strbad_creature = Creature(name="!", description="it's a raccoon", area="your attic")

执行:

Traceback (most recent call last):File D:\ProgramData\anaconda3\lib\site-packages\spyder_kernels\py3compat.py:356 in compat_execexec(code, globals, locals)File d:\code\test5.py:10bad_creature = Creature(name="!", description="it's a raccoon", area="your attic")File ~\AppData\Roaming\Python\Python310\site-packages\pydantic\main.py:176 in __init__self.__pydantic_validator__.validate_python(data, self_instance=self)ValidationError: 3 validation errors for Creature
nameString should have at least 2 characters [type=string_too_short, input_value='!', input_type=str]For further information visit https://errors.pydantic.dev/2.7/v/string_too_short
countryField required [type=missing, input_value={'name': '!', 'descriptio...", 'area': 'your attic'}, input_type=dict]For further information visit https://errors.pydantic.dev/2.7/v/missing
akaField required [type=missing, input_value={'name': '!', 'descriptio...", 'area': 'your attic'}, input_type=dict]For further information visit https://errors.pydantic.dev/2.7/v/missing

Field() 的...参数表示需要一个值,而且没有默认值

这只是对 Pydantic 的简单介绍。主要的收获是,它能让你自动验证数据。在从网络层或数据层获取数据时,您将看到这一点有多么有用。

5.6 小结

模型是定义将在网络应用程序中传递的数据的最佳方式。Pydantic利用Python的类型提示来定义数据模型,以便在应用程序中传递。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/721886.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第四天 |

24. 两两交换链表中的节点 题目:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 解题: 关键:cur的位置在要交换的两个节点的前面 具体如何交换的操作!! while种植条件:cur的下…

C++--移动构造函数/移动赋值运算符

C++ 的移动语义C++--移动构造函数/移动赋值运算符 什么是移动语义?在C++11中,移动语义是一个重要的新特性,它可以使程序在内存管理方面更加高效,同时也提高了程序的性能它允许将一个对象的所有权从一个对象转移到另一个对象,而不需要进行数据的拷贝。 通俗理解 我有一份材…

java小记

今天学了一些计算机存储数据的原理: 文本,图片,声音等存储 编码:gb2312,Unicode,gbk等等。 数据类型相关遇到了一些问题:解决办法大概是:但是对于我来说是无效的,最后还是照着下面来:成功

第二次pta大作业的总结和反思

第二次pta大作业的总结和反思 目录 一.前言 二.题目分析 三.总结 一.前言 第二次作业说实话有点一言难尽啊,因为比较难的缘故吧,有的题甚至做了一个星期也没做完,所以blog有点无从下手,因为有很多题目是现想的,没有经过调试,不知道思路究竟对不对,可能会有一些理解上会些…

1_关于BeanFactory与ApplicationContext的联系和区别

BeanFactory与ApplicationContext 1. 容器和接口 1.1 BeanFactory与ApplicationContext的联系与区别: ConfigurableApplicationContext 是 ApplicationContext的子接口,而ApplicationContext接口又是 BeanFactory 的子接口。因此ConfigurableApplicationContext 接口简介继承…

回溯法求解TSP问题

1.readme 1>python 2>代码基于具体的实例,如有需要可自行修改问题规模为n,不再赘述 2.code 代价矩阵 999表示无穷 arc = [[999, 3, 6, 7], [5, 999, 2, 3], [6, 4, 999, 2], [3, 7, 5, 999]] city 存放除出发点0外的城市 city = [3, 2, 1] 访问标志数组 0-未访问 1-已访…

OpenGL:混合

OpenGL中,混合(Blending)通常是实现物体透明度(Transparency)的一种技术。透明就是说一个物体(或者其中的一部分)不是纯色(Solid Color)的,它的颜色是物体本身的颜色和它背后其它物体的颜色的不同强度结合。一个有色玻璃窗是一个透明的物体,玻璃有它自己的颜色,但它最终的…

下水道拾的沟槽的文章

wow天色将晚,在我看着你的眼里色彩斑斓 五月的气候合往年一样闷热,二中的晚自习太静,于是闲下来了,止不住地去向,好像又来一轮毕业的时候。小学时,谈起毕业总是很害怕的,那时觉得毕业时候一定是要大哭一场,不然就算不上毕业结果那天也如同流水一般平淡地过去了,正如过…

P3756 [CQOI2017] 老C的方块 解题报告

P3756 [CQOI2017] 老C的方块 解题报告oj:https://gxyzoj.com/d/gxyznoi/p/P266 又是网格题,考虑染色:显然可以发现,每一个不合法的图形都可以被染成黄->蓝->特殊边->绿->红,且旋转后同样满足条件 推广到整个棋盘就是:所以是否可以将颜色编号,然后按照上述方法…

[stars-one] 星念轻小说下载器

原文地址: [stars-one] 星念轻小说下载器-Stars-One的杂货小窝 一款将在线轻小说保存到本地的下载工具 软件介绍小说单卷下载 小说全卷下载(需VIP) 多线程解析和下载 下载导出为epub文件 自动更新软件使用前需要进行用户登录(邮箱注册) 采用会员订阅制,PC版和Android版共用账号…

裁剪序列Cut the Sequence

首先,我们可以先想一想朴素算法,推出DP,i表示分了几段,则可以推出$$F[i]=min_{1<=j<=i}(f[j]+max_{j+1<=k<=i}(a[k]))$$点击查看代码memset(f,0x3f,sizeof f);f[0]=0;for(int i=1;i<=n;i++){for(int j=0;j<i;j++){int tmp=0;ll sum=0;for(int k=j+1;k<…

vits-simple-api搭建

根据vits-simple-api中文文档指南自行搭建后端 以下步骤均在windows平台cpu推理搭建为例选择你的vits模型(注意是vits!不是So-Vits Bert Vits2 Gpt Vits)建议去抱脸网搜索或者b站搜素以及自己训练.在vits-simple-api的路径的model目录下新建你下载模型的名字的文件夹将模型的js…