预览
FastAPI主要基于一个名为Pydantic的Python包。 这使用(Python 对象类)来 定义数据结构。 这些在 FastAPI 应用程序中大量使用, 在编写大型应用程序时是一个真正的优势。
类型提示
是时候 了解有关 Python 的更多信息。
第2章提到,在许多计算机语言中, 变量直接指向内存中的值。 这要求程序员声明其类型,因此 可以确定值的大小和位数。 在 Python 中,变量只是与对象关联的名称, 它是具有类型的对象。
在正常编程中, 变量通常与 相同的对象。 如果我们将类型提示与该变量相关联, 我们可以避免一些编程错误。 所以Python在语言中添加了类型提示, 在标准模块键入中。 Python 解释器忽略 该类型提示语法并运行程序,就好像它不存在一样。 那有什么意义呢?
您可以将变量视为一行中的字符串, 稍后忘记并为其分配不同类型的对象。 尽管其他语言的编译器会抱怨, Python不会。 标准的 Python 解释器将捕获 正常语法错误和运行时异常。 但不混合变量的类型。 像 Mypy 这样的辅助工具会注意类型提示, 并警告您 任何不匹配。
此外,提示可供 Python 开发人员使用, 谁可以编写比类型错误检查更多的工具。 以下各节介绍了 Pydantic 软件包如何 是为了满足并不明显的需求而开发的。 稍后,您将看到它如何与FastAPI集成 使许多Web开发问题更容易处理。
顺便问一下,类型提示是什么样的? 变量有一种语法, 另一个用于函数返回值。
变量类型提示可能仅包含以下类型:
name: type
或者还用一个值初始化变量:
name: type = value
可以是标准的 Python 简单类型之一,如 int 和 str 或集合类型,如元组、列表和字典。
thing: str = "yeti"
注意
在 Python 3.9 之前,您需要导入大写版本 来自键入模块的这些标准类型名称:
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}
键入模块具有子类型的有用附加功能, 最常见的是:
- 任何 : 任何类型
- 联合 :指定的任何类型,例如 Union[str, int] 。
注意
在 Python 3.10 及更高版本中,你可以说 type1 |类型2 改为 联合[类型1,类型2]。
例子:
from typing import Any
responses : dict [ str , Any ] = { "Marco" : "Polo" , "answer" : 42 }
或者,更具体一点:
from typing import Union
responses : dict [ str , Union [ str , int ]] = { "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
...
>>> thing1: str = "yeti"
>>> thing1 = 47
但他们会被Mypy抓住。 如果您还没有它,请 pip 安装 mypy . 将上面的这两行保存到名为 的文件中, 然后试试这个:
$ 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:
如:
def get_thing () -> str :
return "yeti"
可以使用任何类型,包括已定义的类, 以及它们的组合。 您将在几页中看到这一点。
数据分组
通常,我们需要将一组相关的变量放在一起, 而不是传递单个变量的日志。 我们如何将多个变量整合为一个组, 并保留类型提示?
让我们抛开前几章中不温不火的问候示例, 并从现在开始使用更丰富的数据。 与本书的其余部分一样, 我们将使用生物(虚构生物)的例子, 以及寻找它们的(也是虚构的)。 我们最初的生物定义将只包括、和的字符串变量。
Python 的历史数据分组结构 (超越基本的整数、字符串等) 是:
- 元组:不可变的对象序列
- 列表:可变的对象序列
- 集合:可变的不同对象
- 字典:可变键:值对象对(键必须是不可变类型)
元组(示例 5-1)和列表(示例 5-2) 仅允许您通过偏移量访问成员变量, 所以你必须记住什么去了哪里:
使用元组
>>> tuple_thing = ( "yeti" , "Abominable Snowman" , "Himalayas" )
>>> print ( "Name is" , tuple_thing [ 0 ])
Name is yeti
使用列表
>>> list_thing = [ "yeti" , "Abominable Snowman" , "Himalayas" ]
>>> print ( "Name is" , list_thing [ 0 ])
Name is yeti
示例 5-3 显示您可以通过以下方式获得更多解释 定义整数偏移量的名称:
使用元组和命名偏移量
>>> NAME = 0
>>> LOCATION = 1
>>> DESCRIPTION = 2
>>> tuple_thing = ( "yeti" , "Abominable Snowman" , "Himalayas" )
>>> print ( "Name is" , tuple_thing [ NAME ])
Name is yeti
字典在示例 5-4 中稍微好一点, 通过描述性键为您提供访问权限:
使用字典
>>> dict_thing = { "name" : "yeti" ,
... "description" : "Abominable Snowman" ,
... "location" : "Himalayas" }
>>> print ( "Name is" , dict_thing [ "name" ])
Name is yeti
集合仅包含唯一值,因此它们对 聚类不同的变量。
在示例 5-5 中,为 允许您通过整数偏移名称进行访问的元组:
例 5-5。
>>> from collections import namedtuple
>>> CreatureNamedTuple = namedtuple("CreatureNamedTuple",
... "name, description, location)
>>> namedtuple_thing = CreatureNamedTuple("yeti",
... "Abominable Snowman",
... "Himalayas")
>>> print("Name is", namedtuple_thing[0])
Name is yeti
>>> print("Name is", namedtuple_thing.name)
Name is yeti
注意
你不能说namedtuple_thing[“名字”]。 这是一个元组,而不是字典, 所以索引需要是一个整数。
例5-6定义了一个新的Python类, 并添加所有 属性与自我 . 但是你需要做很多输入来定义它们:
使用普通类
>>> class CreatureClass ():
... def __init__ ( self , name : str , description : str , location : str ):
... self . name = name
... self . description = description
... self . location = location
...
>>> class_thing = CreatureClass ( "yeti" ,
... "Abominable Snowman" ,
... "Himalayas" )
>>> print ( "Name is" , class_thing . name )
Name is yeti
注意
你可能会想,这有什么不好的? 使用常规类,您可以添加更多数据(属性), 但尤其是行为(方法)。 你可能会决定,有一天疯狂的一天, 添加查找资源管理器的方法 最喜欢的歌曲。 (这不适用于 生物.) 但这里的用例只是移动一堆数据 坦然 在层中,并在进出途中进行验证。 此外,方法是方形钉子 这将很难适应数据库的圆孔。
Python有类似的东西吗 到什么其他计算机语言 调用或(一组名称和值)? Python最近增加的一个是。 示例 5-7 显示了所有这些自我填充的内容 随数据类一起消失:
使用数据类
>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class CreatureDataClass ():
... name : str
... description : str
... location : str
...
>>> dataclass_thing = CreatureDataClass ( "yeti" ,
... "Abominable Snowman" , "Himalayas" )
>>> print ( "Name is" , dataclass_thing . name )
Name is yeti
这对于将变量保持在一起部分非常有用。 但我们想要更多,所以让我们向圣诞老人要求:
- 可能的替代类型的
- 缺失/可选值
- 默认值
- 数据验证
- 与 JSON 等格式的序列化
选择
使用Python内置的数据结构很诱人, 尤其是字典。 但你不可避免地会发现字典有点太“松散”。 自由是有代价的。 您需要检查:
- 密钥是可选的吗?
- 如果缺少密钥,是否有默认值?
- 密钥是否存在?
- 如果是这样,键的值类型是否正确?
- 如果是这样,该值是否在正确的范围内,或者与模式匹配?
至少有三种解决方案可以解决至少 其中一些要求:
- : 标准 Python 的一部分。
- : 第三方,但数据类的超集。
- : 也是第三方,但集成到FastAPI中, 所以如果你已经在使用FastAPI,这是一个简单的选择。 如果你正在读这本书,那很有可能。
在Youtube上对这三者进行了。 一个结论是,Pydantic在验证中脱颖而出, 它与FastAPI的集成抓住了许多潜力 数据错误。 另一个是Pydantic依赖于继承(来自 基本模型类), 另外两个使用Python装饰器来定义他们的对象。 这更多的是风格问题。
在另一个中, Pydantic优于旧的验证包,如和有趣的。 Pydantic的另一大优势是它使用标准 蟒蛇类型提示语法; 较旧的库早于类型提示并自行推出。
所以我在这本书中选择了Pydantic, 但您可能会发现这两种选择的用途 如果您没有使用快速API。
Pydantic 提供了指定这些检查的任意组合的方法:
- 必需与可选
- 默认值(如果未指定但必需)
- 预期的数据类型
- 值范围限制
- 其他基于功能的检查(如果需要)
- 序列化和反序列化
一个简单的例子
你已了解如何将简单字符串馈送到 Web 终结点 通过 URL、查询参数或 HTTP 正文。 问题是通常您通常会请求和接收 多种类型的数据组。
这就是Pydantic模型首次出现在FastAPI中的地方。
此初始示例将使用三个文件:
- 定义了一个 Pydantic 模型
- 是假数据源,定义模型的实例
- 定义了一个返回假数据的 FastAPI Web 端点
为简单起见,本章, 让我们将所有文件保留在同一目录中 目前。 在后面讨论大型网站的章节中, 我将将它们分成各自的层。
首先,定义示例 5-8 中生物的:
定义生物模型:model.py
from pydantic import BaseModel
class Creature ( BaseModel ):
name : str
description : str
location : str
thing = Creature ( name = "yeti" ,
description = "Abominable Snowman" ,
location = "Himalayas" )
print ( "Name is" , thing . name )
Creature 类继承自 Pydantic 的 BaseModel。 那 : str 部分 在名称、位置和描述之后 是一个类型提示,每个都是一个 Python 字符串。
注意
在此示例中,所有三个字段都是必填字段。 在 Pydantic 中,如果类型描述中没有“可选”, 该字段必须具有值。
在示例 5-9 中, 如果包含参数的名称,则按任意顺序传递参数:
创建一个生物
>>> thing = Creature ( name = "yeti" ,
... description = "Abominable Snowman" ,
... location = "Himalayas" )
>>> print ( "Name is" , thing . name )
Name is yeti
目前,示例 5-10 定义了一个很小的数据源; 在后面的章节中,数据库将执行此操作。 类型提示列表[生物] 告诉 Python 这只是一个 Creature 对象列表。
在 data.py 中定义假数据
from model import Creature
_creatures : list [ Creature ] = [
Creature ( name = "yeti" ,
description = "Abominable Snowman" ,
location = "Himalayas" )
Creature ( name = "sasquatch" ,
description = "Bigfoot" ,
location = "North America" )
]
def get_creatures () -> list [ Creature ]:
return _creatures
这段代码导入了我们刚刚编写。 它通过调用其列表来隐藏一些数据 生物对象_creatures,并提供功能 get_creatures() 返回它们。
示例 5-11 列出了 , 定义 FastAPI Web 终结点的文件:
定义快速 API Web 终结点:web.py
from model import Creature
from fastapi import FastAPI
app = FastAPI ()
@app . get ( "/creature" )
def get_all () -> list [ Creature ]:
from data import get_creatures
return get_creatures ()
现在在示例 5-12 中启动此单端点服务器:
启动 Uvicorn
$ uvicorn creature:app
INFO: Started server process [24782]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
在另一个窗口中,示例 5-13 使用 Httpie Web 客户端访问它 (如果您愿意,也可以尝试使用浏览器或请求模块):
使用 Httpie 进行测试
$ http http://localhost:8000/creature
HTTP/1.1 200 OK
content-length: 183
content-type: application/json
date: Mon, 12 Sep 2022 02:21:15 GMT
server: uvicorn
[
{
"description": "Abominable Snowman",
"location": "Himalayas",
"name": "yeti"
},
{
"description": "Bigfoot",
"location": "North America",
"name": "sasquatch"
}
FastAPI 和 Starlette 自动转换了原始内容 生物模型对象列表转换为 JSON 字符串。 这是 FastAPI 中的默认输出格式, 所以我们不需要指定它。
此外,您最初在其中启动的窗口 Uvicorn 网络服务器应该 已打印日志行:
INFO: 127.0.0.1:52375 - "GET /creature HTTP/1.1" 200 OK
验证类型
上一节演示了如何:
- 将类型提示应用于变量和函数
- 定义和使用 Pydantic 模型
- 从数据源返回模型列表
- 将模型列表返回到 Web 客户端, 自动将模型列表转换为 JSON
现在,让我们真正使用它来验证数据。
尝试将错误类型的值分配给一个或多个 的生物领域。 让我们为此使用一个独立的测试 (Pydantic 不回复任何网络代码; 这是一个数据问题)。
示例 5-14 列出了 :
测试生物模型
from model import Creature
dragon = Creature (
name = "dragon" ,
description = [ "incorrect" , "string" , "list" ],
location = "Worldwide"
)
现在在示例 5-15 中尝试一下:
运行测试
$ python test1.py
Traceback (most recent call last):
File ".../test1.py", line 3, in <module>
dragon = Creature(
File "pydantic/main.py", line 342, in
pydantic.main.BaseModel.init
pydantic.error_wrappers.ValidationError:
1 validation error for Creature description
str type expected (type=type_error.str)
这发现我们将字符串列表分配给 描述字段, 它想要一根普通的旧字符串。
验证值
即使值的类型与其规范匹配 在生物类中, 可能需要通过更多检查。 可以对值本身施加一些限制。
- 整数 ( conint ) 或浮点数:
- gt — 大于
- lt — 小于
- ge — 大于或等于
- le — 小于或等于
- multiple_of — 值的整数倍
- 字符串 ( constr ):
- min_length — 最小字符(非字节)长度
- max_length — 最大字符长度
- to_upper — 转换为大写
- to_lower — 转换为小写
- 正则表达式 — 匹配 Python 正则表达式
- 元组、列表或集合:
- min_items — 最小元素数
- max_items — 元素的最大间隔
这些在模型的类型部分中指定。
示例 5-1 确保名称字段始终为 长度至少为两个字符。 否则,“”(空字符串)是有效的字符串。
查看验证失败
>>> from pydantic import BaseModel , constr
>>>
>>> class Creature ( BaseModel ):
... name : constr ( min_length = 2 )
... description : str
... location : str
...
>>> bad_creature = Creature ( name = "!" ,
... description = "it's a raccoon" ,
... location = "your attic" )
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
File "pydantic/main.py" , line 342 ,
in pydantic . main . BaseModel . __init__
pydantic . error_wrappers . ValidationError :
1 validation error for Creature name
ensure this value has at least 2 characters
( type = value_error . any_str . min_length ; limit_value = 2 )
该 constr 表示一个。 示例 5-17 使用替代方法, 皮丹蒂克场规范:
另一个验证失败,使用 Field
>>> from pydantic import BaseModel , Field
>>>
>>> class Creature ( BaseModel ):
... name : str = Field ( ... , min_length = 2 )
... description : str
... location : str
...
>>> bad_creature = Creature ( name = "!" ,
... location = "your attic" ,
... description = "it's a raccoon" )
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
File "pydantic/main.py" , line 342 ,
in pydantic . main . BaseModel . __init__
pydantic . error_wrappers . ValidationError :
1 validation error for Creature name
ensure this value has at least 2 characters
( type = value_error . any_str . min_length ; limit_value = 2 )
那。。。参数到 Field() 表示需要一个值, 并且没有默认值。
这是对Pydantic的最小介绍。 主要要点是它可以让您自动验证 您的数据。 您将看到这在从以下位置获取数据时是多么有用 网络或数据结束。
回顾
模型是定义数据的最佳方式,这些数据将 在 Web 应用程序中传递。 Pydantic 利用 Python 来定义要在应用程序中传递的数据模型。
接下来:定义要分离的 常规代码中的特定详细信息。
本文暂时没有评论,来添加一个吧(●'◡'●)