ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python类型提示如何撑起FastAPI的自动校验与接口文档

Python类型提示如何撑起FastAPI的自动校验与接口文档 很多第一次接触 FastAPI 的朋友都有同一个困惑官方教程第一页不先讲路由、不讲中间件反而花大量篇幅讲 Python 的类型提示Type Hints。我当时学的时候也嘀咕写个接口直接 return 不就行了搞这么复杂干嘛直到真用 FastAPI 写完几个项目被它“自动校验参数、自动生成接口文档、自动做数据序列化”这三板斧震住之后才明白——这些能力的根全扎在 Python 类型提示上。说白了FastAPI 不是靠什么黑魔法它只是特别擅长在运行时读取你写在函数签名里的类型信息然后把它们变成真正干活儿的校验规则。所以这一篇我打算把 Python 类型这件事掰开揉碎地讲清楚包括基础语法、typing 模块的演进、Enum 枚举怎么用以及类型提示在 FastAPI 里到底是怎么被“翻译”成校验逻辑的。这个系列后续会讲路由、参数、Pydantic 模型和项目实战如果你能把这篇吸收掉后面会顺很多。适合谁来读呢准备用 FastAPI 写后端接口的朋友尤其是写 Python 但平常几乎不用类型标注的同学。已经会写类型注解的读者可以跳到第 3 节看 FastAPI 的原理不过里面有几个容易踩的坑我建议还是通读一遍。1. 为什么说类型提示是 FastAPI 的“地基”1.1 一个最简单例子背后的庞大逻辑先看 FastAPI 官方文档里那个出现频率最高的例子from fastapi import FastAPI app FastAPI() app.get(/items/{item_id}) async def read_item(item_id: int): return {item_id: item_id}你启动这个服务后访问/items/123返回 JSON{item_id: 123}。如果你访问/items/abcFastAPI 不会傻乎乎地把abc塞给你而是直接返回一个 422 错误告诉你这个参数类型不对。这里就藏着 FastAPI 的核心设计逻辑它拿到item_id: int这个标注后会做三件事——把item_id识别为路径参数从 URL 上抓取对应的字符串根据类型注解int尝试把字符串123转成整数123转换失败时生成一条结构化的校验错误信息并以 HTTP 422 状态码返回。你可以试试把注解改成item_id: float那/items/3.14就能通过校验。换成item_id: bool你甚至能看到 FastAPI 把1、0、yes、no等字符串自动映射成 True 或 False。这就是类型提示在 FastAPI 里的地位它不是写给人看的备注而是被框架在运行时真实执行的规则。1.2 动态类型语言的自律难题说句公道话Python 作为动态类型语言写起来确实爽。定义一个函数传字符串也行、传数字也行解释器都不拦着。但项目一变大问题就来了。我见过不少真实事故有人把一个字段从int改成了str结果另一端还在做数值运算跑起来直接报 TypeError有人写了一个处理订单金额的函数调用方不小心传入带逗号的字符串1,234前端展示看不出问题最后对账差了十几万。这类问题在静态语言里编译期就暴露了在 Python 里却要等到线上运行到那一行才爆。类型提示不会把 Python 变成 Java 或者 TypeScript但它能在开发阶段给 IDE、给静态检查工具比如 mypy提供足够的信息提前拦截一大批低级错误。更重要的是FastAPI 把类型提示变成了运行时契约的一部分。你写清楚参数类型框架就帮你做校验你写清楚响应模型框架就帮你做过滤。这是一种“动态语言的自由 静态语言的约束”的折中方案既保留开发效率又给接口立了规矩。1.3 类型提示的演进简史与时代选择如果你搜过 Python 类型相关的内容大概率见过两种截然不同的写法List[str]和list[str]。这不是谁对谁错而是版本演进的结果。Python 3.5PEP 484 引入typing模块标准写法是typing.List[str]、typing.Dict[str, int]Python 3.9PEP 585 让内置类型直接支持泛型于是list[str]、dict[str, int]成为合法写法Python 3.10PEP 604 引入X | Y语法str | None从此可以替代typing.Optional[str]Python 3.11进一步完善了各种类型细节typing模块的功能愈发成熟。FastAPI 本身对运行环境的要求不算苛刻但我的建议是新项目直接用 Python 3.10 以上代码里书写类型时尽量用内置泛型语法。原因很简单——list[str]更短、更直观、少一层导入配合 Pydantic v2 时兼容性也更好。我看过不少老项目还保留着typing.List和typing.Dict的写法那是 Python 3.8 时代迁移过来的遗产能跑但没必要在新代码里继续用。2. Python 类型提示核心语法拆解2.1 基础类型注解与 bool 的隐藏坑基础类型注解是最简单的一层直接在变量、函数参数和返回值后面加冒号和类型就行# 变量注解 count: int 10 name: str FastAPI price: float 29.9 is_active: bool True # 函数参数与返回值注解 def add(a: int, b: int) - int: return a b def get_username(user_id: int) - str: return fuser_{user_id}看起来平平无奇但这里有一个很多教程不会提的细节Python 里bool是int的子类。这意味着isinstance(True, int)的结果是True。在写类型判断的时候如果你先判断int再判断boolTrue会被int分支截胡。举个例子我写过一段对接口入参做兜底清理的代码def clean_value(v) - int: if isinstance(v, bool): # 必须放在 int 前面 return int(v) if isinstance(v, int): return v return 0如果我把两个isinstance的顺序颠倒传进来一个True它会被当成整数 1 直接返回看不出问题但等你在某处把 1 当数值处理时语义就悄悄变了。这个坑在日常业务里不一定触发但你写 FastAPI 的Query校验、写 Pydantic 自定义校验器时一旦涉及bool和int的边界就容易中招。2.2 容器类型List、Dict、Set、Tuple单值类型只是开胃菜接口开发里最常用的是容器类型。声明一个元素都是字符串的列表、一个键值对都是特定类型的字典可以这样写from typing import List, Dict, Set, Tuple # Python 3.9 推荐写法 tags: list[str] [python, fastapi, api] user_scores: dict[str, int] {alice: 95, bob: 88} unique_ids: set[int] {1, 2, 3} pair: tuple[str, int] (alice, 95)要注意的是list[str]表示“列表里的每个元素都是字符串”它不做运行时强制——你往列表里塞一个整数解释器照样不报错。类型提示在普通 Python 代码里更多是给静态检查工具和 IDE 提供信息。但在 FastAPI 的 Pydantic 模型里情况就不一样了Pydantic 会真的去遍历列表、校验每个元素类型失败就报错。所以容器类型在不同场景下的“约束力”是完全不同的这一点到了第 3 节你会感受很深。还有一个值得注意的写法是tuple。tuple[str, int]表示二元组分别是 str 和 int而tuple[str, ...]表示元素全是 str、长度不限的元组。用到 FastAPI 里做参数校验时这两种语义差别挺大别写混了。2.3 Union、Optional、Any 与类型收窄接口参数经常面临“可能是这个、也可能是那个”的情况Union 就是为此设计的from typing import Union, Optional, Any # 旧写法 value: Union[int, str] 1 # Python 3.10 推荐写法 value: int | str 1 # Optional 其实等价于 类型 | None nickname: Optional[str] None nickname: str | None None很多初学者容易把Optional[str]理解为“可选的字符串参数”其实它的准确含义是“这个值要么是字符串、要么是 None”。参数是否为“可选”取决于函数定义里有没有给默认值。看这个对比# 参数必填但允许传 null def f1(x: str | None): pass # 参数可选不传时为 None def f2(x: str | None None): pass在 FastAPI 里这个区别直接影响 OpenAPI 文档里参数的required字段。你写x: str | None无默认值接口文档会把这个参数标成必填你写x: str | None None才会变成选填。很多人查了半天文档发现参数明明是可选却一直报缺少参数原因就是漏了等号后面的默认值。Any 则是彻底“摆烂”的类型表示什么类型都行。我不建议在接口层使用 Any因为一旦用它FastAPI 就失去了校验依据。我曾经在一个老项目里见过满满一屏Any最后接口文档基本等于没有线上报错全靠日志硬猜——这是类型提示最没价值的用法。类型收窄narrowing是配合 Union 使用的一个习惯。Python 解释器不会自动帮你区分Union[int, str]里的具体类型但你可以用isinstance做判断静态检查工具能顺着这个判断推导出分支里的确切类型def parse(value: int | str) - int: if isinstance(value, str): return len(value) # 此处 value 被推断为 str else: return value * 10 # 此处 value 被推断为 int2.4 枚举类型 Enum 与字符串转换的特殊细节枚举类型在接口开发里太常用了。订单状态、用户角色、商品分类这些取值有限的状态字段用Enum表达比用裸字符串安全得多。先看基本定义from enum import Enum class Status(str, Enum): pending pending paid paid shipped shipped cancelled cancelled这里有个设计细节class Status(str, Enum)继承自 str 又继承自 Enum这是专门为了让枚举成员既支持字符串比较、又能直接当字符串用。如果你写成class Status(Enum)那Status.paid就是一个普通枚举对象把它传给需要字符串的第三方库时经常要手动转。枚举转字符串是最容易被坑的地方热词榜上“枚举类型转换为字符串”常年有热度不是没原因的。直接看现象s Status.paid print(s.value) # paid print(str(s)) # 在 Python 3.10 及以下Status.paid # 在 Python 3.11 及以上paid print(f{s}) # 同样受版本影响也就是说str(s)的结果在 Python 3.11 前后是不一样的。3.11 引入了针对 str-mixin 枚举的格式化改进str(成员)会返回成员值老版本则返回Status.paid这种带类名前缀的表示。所以跨版本项目里最安全的做法是永远取.value别依赖str()的格式化结果。FastAPI 内部处理枚举时有一套自己的逻辑一般不会让你手动转但你要是写日志、拼字符串、存数据库这里就很容易埋雷。FastAPI 对枚举参数做了额外支持你定义一个枚举类型作为参数注解框架会在校验时自动检查传值是否在枚举成员值的范围内若不在就返回 422。这个特性配合strmixin 食用效果最好成员值可读、可直接序列化。2.5 泛型TypeVar 与 Generic泛型在 FastAPI 日常开发里用得不算频繁但理解它有助于你看懂第三方库的类型声明也能帮你写更灵活的复用代码。简单说泛型就是“类型的占位符”from typing import TypeVar, Generic T TypeVar(T) class Box(Generic[T]): def __init__(self, content: T): self.content content def get(self) - T: return self.content int_box Box(123) # Box[int] str_box Box(hello) # Box[str]如果你静态检查过代码会看到int_box.get()被推断为intstr_box.get()被推断为str。这就是泛型的价值一段代码适用于多种类型同时还能保留类型信息。在 FastAPI 场景里list[Item]、dict[str, Item]这种组合式泛型用法更常见它们底层也是泛型机制。还有个知识TypeVar可以限定上界比如T TypeVar(T, boundBaseModel)表示只接受BaseModel及其子类。写通用工具函数时很实用能提前拦住不合理的类型传参。3. 类型提示在 FastAPI 里到底是怎么被使用的3.1 路径参数解析、转换与校验一体化回到 FastAPI路径参数是最直观的类型应用场景from fastapi import FastAPI app FastAPI() app.get(/users/{user_id}) async def get_user(user_id: int): return {user_id: user_id, type: type(user_id).__name__}请求/users/42返回{user_id: 42, type: int}。你看到没有路径上本来全是字符串但 FastAPI 根据user_id: int做了转换函数内部拿到的已经是真正的整数。如果你给user_id注解float路径/users/3.8也能过。你要是访问/users/fastapi就会收到 422返回体大致长这样{ detail: [ { type: int_parsing, loc: [path, user_id], msg: Input should be a valid integer, unable to parse string as an integer, input: fastapi } ] }很多初学者第一次看到 422 会懵觉得“我路径写错了不是该返回 404 吗”这就是 FastAPI 的设计逻辑路径匹配到了路由但参数校验没过所以是请求本身非法返回 422。想明白了这一点后续排查问题会顺手很多。3.2 查询参数默认值是“可选”的关键查询参数Query Parameter的处理逻辑与路径参数类似区别在于默认值app.get(/items/) async def read_items( page: int 1, page_size: int 10, keyword: str | None None, sort_desc: bool False ): return { page: page, page_size: page_size, keyword: keyword, sort_desc: sort_desc, }这里能看出 FastAPI 的规则有默认值的参数是可选的没有默认值的参数是必填的。所以/items/?page2page_size20是合法请求/items/?keyword手机也能正常访问但如果你把某个参数声明成page: int没有默认值那访问/items/就会返回 422提示缺少 page。bool类型作为查询参数时FastAPI 有一套宽松的字符串转布尔逻辑。实际试下来true、1、yes、on会转成 Truefalse、0、no、off会转成 False。这里有个容易忽略的点FastAPI 对bool的解析在一定版本上比较粗放我曾经看到?flagabc这种非法值时有的版本返回 False有的版本直接 422。如果你依赖布尔参数做权限判断最好在业务代码里再校验一次别把逻辑安全的宝全押在框架的隐式转换上。关于str | None None我之前提过它和Optional[str] None完全等价选一个你喜欢的风格就行。但千万别写keyword: str None这种写法虽然运行时能跑但类型检查器会亮红灯——字符串类型不允许 None 作为默认值。3.3 请求体Pydantic 模型才是真正的“类型契约”请求体是 FastAPI 类型系统发挥最大价值的地方。为什么需要 Pydantic因为请求体通常是一整个 JSON 对象里面包含多个字段、嵌套结构、各种类型的组合。如果用单个类型标注很难表达完整的结构。Pydantic 的BaseModel本质上是用类属性加类型注解来声明数据模型from pydantic import BaseModel class ItemCreate(BaseModel): name: str description: str | None None price: float tax: float | None None tags: list[str] []在路由中把它作为参数类型app.post(/items/) async def create_item(item: ItemCreate): return { name: item.name, price: item.price, has_tax: item.tax is not None, }FastAPI 看到item: ItemCreate之后会自动把请求体 JSON 解析出来按照ItemCreate里字段的类型声明逐一校验、转换。比如前端传price: 39.9字符串Pydantic 会把它转成浮点数 39.9如果传price: abc就直接 422。这里特别提醒一个坑如果你把字段类型写成list[str]但前端传了一个数组里面混着字符串和数字Pydantic 会直接报错不会默默帮你转换数字。这是有意为之——接口数据越严格后端代码越省心。嵌套模型也很常见class Address(BaseModel): city: str street: str class UserCreate(BaseModel): name: str age: int address: AddressPydantic 会递归校验嵌套结构。前端传的address如果不是对象类型校验立刻失败。这种层层类型声明让接口的输入结构变得非常清楚比手写一坨dict.get()然后if not isinstance(...)靠谱得多。3.4 响应模型让接口输出的类型也有边界FastAPI 在定义路由时可以声明返回模型from pydantic import BaseModel class UserIn(BaseModel): username: str password: str class UserOut(BaseModel): username: str app.post(/users/, response_modelUserOut) async def create_user(user: UserIn): return user注意到没有函数内部直接返回了UserIn对象里面包含密码字段。但因为有response_modelUserOutFastAPI 会在返回响应时只保留UserOut里声明的字段password被过滤掉了。这个机制在“防止接口把敏感字段暴露出去”的场景里非常有用。我实际开发中习惯给所有响应都定义模型哪怕只是class StatusOut(BaseModel): ok: bool。好处有二一是接口文档里的响应结构清晰二是前端对接时直接看模型就知道会拿到什么。当然响应模型也会做类型转换和校验返回数据不符合模型声明时FastAPI 会抛异常而不是默默返回坏数据。3.5 依赖注入与类型标记FastAPI 的依赖注入也依赖类型提示。看这个最简单的例子from fastapi import Depends, Header async def verify_token(x_token: str Header(...)): # 这里可以查数据库、校验Token等 return x_token app.get(/secure) async def secure_endpoint(token: str Depends(verify_token)): return {token: token}FastAPI 通过Depends(verify_token)知道要调用这个依赖函数然后依赖函数里的x_token: str Header(...)又告诉 FastAPI 它需要从请求头里读取X-Token字段。类型提示在这里贯穿了整条调用链。你给依赖函数的返回值加上类型注解FastAPI 就会把返回值注入给下一个函数的同名参数。出错时FastAPI 也能生成准确的错误信息而不是让你在一堆返回 dict 的代码里靠猜。4. 实操搭一个带类型校验的 FastAPI 小项目4.1 环境准备与安装先说环境。推荐 Python 3.10 以上这里以 Python 3.11 为例。安装依赖极其简单pip install fastapi uvicorn如果你希望后续使用 pydantic 的高级校验功能一般会自动装上。装完可以验证一下python -c import fastapi; print(fastapi.__version__)然后需要 uvicorn 作为 ASGI 服务器来运行应用。开发阶段可以加上--reload代码改了自动重启省得手动来回启停uvicorn main:app --reload4.2 一个完整的示例项目为了把前面的类型知识串起来我写一个带枚举、请求体、查询参数、响应模型的完整示例。目录结构不用复杂对新手来说一个main.py足够# main.py from enum import Enum from fastapi import FastAPI, Query from pydantic import BaseModel app FastAPI(title类型实战演示) class Category(str, Enum): electronics electronics clothing clothing books books class ItemCreate(BaseModel): name: str category: Category price: float Query(gt0) tags: list[str] [] class ItemOut(BaseModel): name: str category: str price: float app.get(/items/{item_id}) async def get_item( item_id: int, q: str | None None, page: int 1, page_size: int Query(default10, ge1, le50), ): return { item_id: item_id, q: q, page: page, page_size: page_size, } app.post(/items/, response_modelItemOut) async def create_item(item: ItemCreate): # 这里可以写入库逻辑 return ItemOut( nameitem.name, categoryitem.category.value, priceitem.price, )注意几个细节Category(str, Enum)让 category 字段既能被 Pydantic 校验为合法的枚举成员值又能在响应模型里直接取出字符串item.category.value。Query(gt0)给 price 加了一个大于 0 的约束这是 FastAPI 基于类型系统扩展的校验功能。page_size: int Query(default10, ge1, le50)表示默认 10且限定在 1 到 50 之间。4.3 用实测数据验证类型系统的作用启动服务后我用 curl 做了几个实验。第一个是合法请求curl -X POST http://127.0.0.1:8000/items/ \ -H Content-Type: application/json \ -d {name:iPhone,category:electronics,price:5999,tags:[phone,apple]}返回{name:iPhone,category:electronics,price:5999.0}第二个是故意传错类别curl -X POST http://127.0.0.1:8000/items/ \ -H Content-Type: application/json \ -d {name:iPhone,category:food,price:1}返回 422错误信息里明确写着category: Input should be electronics, clothing or books。这就是枚举类型的自动校验你没写一行 if 判断。第三个是测试路径参数类型curl http://127.0.0.1:8000/items/abc同样返回 422提示item_id无法解析为整数。你发现没有FastAPI 把这些校验逻辑全部集中到了框架层业务代码里干净得不像动态语言的项目。4.4 用 Swagger 文档反向验证类型声明启动服务后访问http://127.0.0.1:8000/docs你会看到一个自动生成的交互式 API 文档。这个文档不是写死的而是 FastAPI 根据所有路由函数的类型声明实时生成的 OpenAPI schema。你在文档页面上能看到每个路径参数的类型、每个请求体字段的类型与是否必填、每个枚举的合法取值。我建议初学者养成一个习惯写完接口先打开/docs看一眼检查参数的required标记、字段类型、枚举取值是否符合预期。很多时候你以为自己把类型写对了但接口文档会诚实地暴露出错误比看代码更直观。4.5 类型系统在项目结构中的位置等到项目规模稍微大了我建议把类型模型单独拆文件管理目录结构可以这样my_app/ ├── main.py ├── schemas/ │ ├── __init__.py │ ├── item.py │ └── order.py ├── routers/ │ ├── __init__.py │ ├── items.py │ └── orders.py └── models/ ├── __init__.py └── db_models.pyschemas目录专门放 Pydantic 模型请求体、响应体routers放路由函数models放数据库模型。这样分层的核心好处之一就是“类型”这件事有固定归属团队成员都知道 DTO 定义在哪、该怎么复用。类型提示在这个阶段已经不是单个函数的细节而是整个项目接口契约的骨架。5. 踩过的坑类型相关的实战排查记录5.1 枚举转字符串的版本差异这个坑我在第 2.4 节已经强调过这里再补充一个真实案例。之前维护的一个服务在 Python 3.8 上运行日志里打印str(Status.paid)输出Status.paid一直没人觉得有问题。后来部署环境升级到 Python 3.11日志变成paid。当时对接方正好在排查数据格式看到日志格式变了还以为是逻辑被改坏了。如果不希望日志、消息队列里的枚举格式受 Python 版本影响统一用.value。另外如果你在 Pydantic 模型里直接放枚举类型序列化成 JSON 时 Pydantic 会自动取.value这块反而很安全不安全的是你自己手写的字符串拼接逻辑。5.2 Optional 不等于“可选参数”这个误区几乎每个月都能在技术群里看到一次。很多人写def get_item(item_id: int, nickname: Optional[str]): ...以为有Optional就代表 nickname 可以不传。错了这只是说 nickname 可以是 None。要让它可选必须给默认值def get_item(item_id: int, nickname: Optional[str] None): ...在 FastAPI 里这个差异会直接影响接口行为。没有默认值的参数会被标记为必填前端少传一个字段就收到 422。我见过一个线上问题开发明明想在请求体里做一个可选字段结果漏了 None前端升级后疯狂报错排查半天才发现是类型标注写错了。所以看到Optional第一反应应该是“这里允许空值”而不是“这里可以省略”。5.3 可变默认值的坑Python 函数定义时默认值只会被计算一次。如果你写class ItemCreate(BaseModel): tags: list[str] []每个新建的模型实例会共享同一个空列表对象吗在纯 Python 函数里这绝对是个坑Pydantic 内部做了一些保护但省心起见还是显式用Field(default_factorylist)from pydantic import BaseModel, Field class ItemCreate(BaseModel): tags: list[str] Field(default_factorylist)同理你写def add_item(item: ItemCreate, history: list [])这种普通函数签名时可变默认值更是大忌。这不仅是类型问题是 Python 语法层面的经典陷阱顺手养成熟练肌肉记忆省得日后排查诡异共享状态。5.4 Pydantic v1 与 v2 的类型解析差异FastAPI 新版本默认基于 Pydantic v2老项目里可能还是 v1。两者对类型注解的解析有一些差异最典型的就是自定义校验器装饰器从validator变成了field_validator。类型层面Pydantic v2 对list[str]等内置泛型的支持比 v1 更好也更推荐使用。如果你维护老项目一时半会升不了级先记住一点写模型时尽量使用 Pydantic 文档推荐的写法不要混用typing.List和list。有些老代码里两种写法混着来表面上能运行但在复杂继承、泛型嵌套场景下解析行为会变得难预测。5.5 不要滥用 Any 和 Dict我见过不少 FastAPI 项目请求体模型里放一个payload: dict就完事了。这样写接口文档基本废了校验也没了前端传什么后端收什么跟直接用 Flask 没区别。FastAPI 的优势就在于用类型声明把边界立起来一旦退回dict、Any等于放弃了这个框架最值钱的部分。如果确实有一个动态结构的需求可以用dict[str, Any]至少声明键的类型是字符串。更进一步建议用Json类型配合自定义校验或者把可选字段全部显式声明为| None。别嫌麻烦接口的每一处类型声明都是在替未来的你减少排查成本。5.6 uvicorn 日志与 reload 模式的干扰这个话题严格说不属于类型但排查类型相关报错时经常被它干扰。如果你在--reload模式下运行 uvicorn代码保存后进程会重启有时候后端抛的异常日志会短暂丢失或者重复打印看起来像程序没反应。遇到这种情况先别急着怀疑代码逻辑可以把--reload去掉跑一次观察完整堆栈。我调试 Pydantic 校验错误时通常会新开一个终端用curl打接口然后盯着终端日志看 422 的 detail 信息比前端控制台干净得多。5.7 类型检查工具让问题在运行前暴露FastAPI 帮你在运行时做了大量类型校验但业务函数内部的变量类型它管不着。如果你想进一步降低出错率建议引入 mypypip install mypy mypy main.pymypy 能检查出诸如str类型变量上做加减法、把None传给不接受 None 的参数、漏掉分支返回值等问题。在 FastAPI 项目里我通常配合pydantic插件一起用mypy --pluginpydantic.mypy main.py这能让 mypy 更准确地理解 Pydantic 模型的类型行为。当然不是每个项目都必须上 mypy但配合类型提示它能把本属于“运行时才炸”的错误提前搬进编辑器里长期收益非常明显。6. 个人经验与后续安排6.1 我给新项目定下的三条类型规矩踩过不少坑之后我给自己写 FastAPI 代码定了三条规矩。第一条所有接口参数和响应必须声明类型禁止裸dict出入参。哪怕是健康检查接口我也至少返回一个StatusOut模型。刚开始会觉得繁琐但接口文档的可用性是成倍提升的。第二条枚举值一律用strmixin存取数据库、对外输出、记录日志时统一.value。这能避免大量隐式转换问题也让接口文档里的取值一目了然。第三条能用str | None就不写Optional[str]能用list[str]就不写typing.List[str]。这不是强迫症而是让代码风格跟上 Python 现代写法减少新旧写法混用带来的理解成本。6.2 这套类型基础还能怎么延伸这篇把 Python 类型的基础和 FastAPI 里的应用场景串了一遍。你掌握了这些下一步看请求参数的高级用法Query、Path、Body 的元数据声明、看 Pydantic 的字段校验器和模型继承理解起来都会顺畅得多。类型提示在 FastAPI 里像一张网把路由、参数、请求体、响应体、依赖注入全部串在一起你越早习惯在定义函数时顺手写清类型后面的代码就越省心。我个人在实际操作中最深的一点体会是类型提示不是写给别人看的装饰品也不是为了应付面试的知识点而是一种把“接口合同”显式化的手段。在动态语言里写接口最怕的就是数据在系统间流转时悄悄变了形状类型系统至少能把变化控制在明面上。下一篇我们进入请求参数详解把Query、Path、Body一个个讲透到时候你会发现今天这篇类型基础几乎是所有内容的钥匙。
返回列表