数据校验
借助 Pydantic,使用标准的 Python 类型来声明请求体。
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str price: float is_offer: bool | None = None @app.get("/") def read_root(): return {"Hello": "World"} @app.get("/items/{item_id}") def read_item(item_id: int, q: str | None = None): return {"item_id": item_id, "q": q} @app.put("/items/{item_id}") def update_item(item_id: int, item: Item): return {"item_name": item.name, "item_id": item_id}Python语法特性
类型提示
Python 支持可选的“类型提示”(也叫“类型注解”)。
无类型注解
def get_full_name(first_name, last_name): full_name = first_name.title() + " " + last_name.title() return full_name print(get_full_name("john", "doe"))函数功能:
接收
first_name和last_name。通过
title()将每个参数的第一个字母转换为大写。用一个空格将它们拼接起来。
引入类型注解
如果需要调用“把首字母变大写的方法”。是
upper?是uppercase?first_uppercase?还是capitalize?IDE 完全没有提示,因为IDE也不知道这个参数类型,所以不知道能调用哪些方法。为了解决这个问题,引入类型注解;
def get_full_name(first_name: str, last_name: str): full_name = first_name.title() + " " + last_name.title() return full_name print(get_full_name("john", "doe"))因为编辑器知道变量的类型,你不仅能得到补全,还能获得错误检查:
比如传参的时候传入错误类型参数,会触发检查
简单类型注解
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes): return item_a, item_b, item_c, item_d, item_e
注意:Python3 ❌ 没有 long 类型,Python3 的
int就是任意精度的整数; 可以直接接收数据库 BIGINT 类型数据
typing模块
在一些额外的用例中,你可能需要从标准库的typing模块导入内容。比如当你想声明“任意类型”时,可以使用typing中的Any:
from typing import Any def some_function(data: Any): print(data)
泛型类型
有些类型可以在方括号中接收“类型参数”(type parameters),用于声明其内部值的类型。比如“字符串列表”可以写为list[str]。
列表
def process_items(items: list[str]): for item in items: print(item)
元组&集合
def process_items(items_t: tuple[int, int, str], items_s: set[bytes]): return items_t, items_s
字典
def process_items(prices: dict[str, float]): for item_name, item_price in prices.items(): print(item_name) print(item_price)
Union
可以声明一个变量可以是若干种类型中的任意一种,比如既可以是int** 也可以是str。定义时使用竖线(|)把两种类型分开。这称为“联合类型”(union**),因为变量可以是这两类类型集合的并集中的任意一个。
def process_item(item: int | str): print(item)
None
def say_hi(name: str | None = None): if name is not None: print(f"Hey {name}!") else: print("Hello World")类型作为类型
class Person: def __init__(self, name: str): self.name = name def get_person_name(one_person: Person): return one_person.name
Pydantic 模型
Pydantic 是一个用于执行数据校验的 Python 库。
FastAPI完全建立在 Pydantic 之上。
你将数据的“结构”声明为带有属性的类。每个属性都有一个类型。然后你用一些值创建这个类的实例,它会校验这些值,并在需要时把它们转换为合适的类型,返回一个包含所有数据的对象。
你还能对这个结果对象获得完整的编辑器支持。
from datetime import datetime from pydantic import BaseModel class User(BaseModel): id: int name: str = "John Doe" signup_ts: datetime | None = None friends: list[int] = [] external_data = { "id": "123", "signup_ts": "2017-06-01 12:22", "friends": [1, "2", b"3"], } user = User(**external_data) print(user) # > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3] print(user.id) # > 123带元数据注解的类型提示
Python 还提供了一个特性,可以使用Annotated在这些类型提示中放入额外的元数据。
你可以从typing导入Annotated。
from typing import Annotated def say_hello(name: Annotated[str, "this is just metadata"]) -> str: return f"Hello {name}"
Python 本身不会对这个
Annotated做任何处理。对于编辑器和其他工具,类型仍然是str。但你可以在
Annotated中为FastAPI提供额外的元数据,来描述你希望应用如何行为。重要的是要记住:
传给
Annotated的第一个类型参数才是实际类型。其余的只是给其他工具用的元数据。现在你只需要知道
Annotated** 的存在,并且它是标准 Python。😎**
async/await
如果你正在使用第三方库,它们会告诉你使用await关键字来调用它们,就像这样:
# 阻塞等待这个函数操作完; 异步函数必须等待执行完,才能在下面拿到结果 results = await some_library()
然后,通过async def声明你的路径操作函数:
@app.get('/') async def read_results(): results = await some_library() return results如果你正在使用一个第三方库和某些组件(比如:数据库、API、文件系统...)进行通信,第三方库又不支持使用await(目前大多数数据库三方库都是这样),这种情况你可以像平常那样使用def声明一个路径操作函数,就像这样:
@app.get('/') def results(): results = some_library() return resultsPython 的现代版本支持通过一种叫"协程"——使用
async和await语法的东西来写”异步代码“。Starlette (和FastAPI) 是基于 AnyIO 实现的,这使得它们可以兼容 Python 的标准库 asyncio 和 Trio。
FastAPI 中,推荐请求处理方法全部使用
async def来声明