尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
FastAPI 教程:将 Python 类用作依赖(Class as Dependencies)——从 dict 到类型安全的依赖注入实践
FastAPI 教程将 Python 类用作依赖Class as Dependencies——从 dict 到类型安全的依赖注入实践【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi依赖不一定非是函数不可只要是一个 Python 可调用对象callableFastAPI 就会像解析路径操作函数的参数一样去解析它。本指南以本仓库官方教程文档 classes-as-dependencies.md 为主线演示如何把前文依赖示例中的dict返回改为一个携带__init__参数的类从而让编辑器获得完整的代码补全与类型检查能力并讲解Depends()无参简写背后的解析规则。读完你将掌握类即依赖的声明方式、类型注解与Depends各自承担的职责以及 FastAPI 底层对 callable 的统一处理机制。一个dict能做什么从上一个示例说起在依赖注入教程的上一部分对应 tutorial001_an_py310.py我们的依赖函数dependable返回的是一个dictfrom typing import Annotated from fastapi import Depends, FastAPI app FastAPI() async def common_parameters(q: str | None None, skip: int 0, limit: int 100): return {q: q, skip: skip, limit: limit} app.get(/items/) async def read_items(commons: Annotated[dict, Depends(common_parameters)]): return commons app.get(/users/) async def read_users(commons: Annotated[dict, Depends(common_parameters)]): return commons于是read_items/read_users路径操作函数参数commons中拿到的就是一个dict。问题随之而来编辑器无法给dict提供太多支持比如补全因为它根本不知道这个字典里有哪些键、每个键的值是什么类型。commons[q]这种写法既没有补全提示也无法做静态类型检查。我们可以做得更好——用一个真正的 Python 类来替代。什么构成一个依赖关键在可调用对象到目前为止教程中出现的依赖都是用函数声明的async def common_parameters(...): ...但函数不是声明依赖的唯一方式虽然它可能是最常见的一种。决定性的因素只有一个依赖应当是一个可调用对象callable。在 Python 里callable指任何可以像函数一样被调用的东西。只要存在一个对象something它可能不是函数而你能够这样执行它something()或者something(some_argument, some_keyword_argumentfoo)那么something就是一个可调用对象。类也是可调用对象细想一下创建一个 Python 类的实例用的正是同一套调用语法class Cat: def __init__(self, name: str): self.name name fluffy Cat(nameMr Fluffy)这里fluffy是Cat类的一个实例而要制造出fluffy你正是在调用Cat。也就是说Python 类天然就是一个可调用对象。因此在 FastAPI 中可以直接把 Python 类当作依赖来用。FastAPI 实际校验的只有两件事传进来的东西是否是一个callable函数、类或任何其它可调用对象它声明了哪些参数。如果你把一个 callable 作为依赖传给 FastAPIFastAPI 会分析该 callable 的参数并按与路径操作函数参数完全相同的方式去处理它们——包括嵌套子依赖。这个规则同样适用于完全没有参数的 callable正如无参数的路径操作函数一样。从源码看FastAPI 在 fastapi/dependencies/utils.py 中的get_dependant()第 271 行起与analyze_param()第 381 行起完成这套解析它们对 dependency 的call统一分析签名、提取子依赖并逐参数处理而并不关心这个 callable 到底是def函数、class还是别的可调用形式。get_dependant中还会通过_is_async_gen_callable、_is_gen_callable等工具判定依赖属于普通协程、同步函数还是生成器变体见 fastapi/dependencies/utils.py 对parameter-less dependency must have a callable dependency的断言。用类声明依赖__init__即依赖的参数现在把上面的依赖common_parameters改写成类CommonQueryParams。完整示例见 tutorial002_an_py310.pyfrom typing import Annotated from fastapi import Depends, FastAPI app FastAPI() fake_items_db [{item_name: Foo}, {item_name: Bar}, {item_name: Baz}] class CommonQueryParams: def __init__(self, q: str | None None, skip: int 0, limit: int 100): self.q q self.skip skip self.limit limit app.get(/items/) async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]): response {} if commons.q: response.update({q: commons.q}) items fake_items_db[commons.skip : commons.skip commons.limit] response.update({items: items}) return response请注意用来创建实例的__init__方法def __init__(self, q: str | None None, skip: int 0, limit: int 100):它的参数与之前的函数common_parameters完全一致。这些参数正是 FastAPI 用来求解依赖的输入也就是说FastAPI 会从__init__的签名中提取查询参数的声明。两种写法最终得到的是完全相同的三件事一个可选的q查询参数类型为str一个skip查询参数类型为int默认值0一个limit查询参数类型为int默认值100。两种写法下这些数据都会被同样地转换类型、校验、写入 OpenAPI schema 文档等等。唯一区别在于函数版本把它们塞进一个dict类版本则把它们绑定为实例属性进而在路径操作函数里可以直接通过commons.q、commons.skip访问。使用这个类依赖现在即可用这个类声明依赖app.get(/items/) async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):FastAPI 会调用CommonQueryParams类、创建一个该类的实例并把实例作为参数commons传入你的函数。__init__中写明的每个参数都会从请求中解析——q、skip、limit从查询字符串而来并在实例化前完成类型转换与校验。这样路由处理函数拿到的是一个具有真实类型CommonQueryParams的对象编辑器能基于属性类型提供commons.q、commons.skip、commons.limit的补全拼错属性名也会被静态检查捕获。类型注解与Depends各司其职请观察上面代码里CommonQueryParams出现了两次commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]Python 3.10 非Annotated写法为commons: CommonQueryParams Depends(CommonQueryParams)教程的 tip 建议尽可能使用Annotated版本。第二个CommonQueryParams即Depends(CommonQueryParams)里的那个才是 FastAPI 真正用来识别依赖的它会从这一个里提取声明的参数最终也是调用它。第一个CommonQueryParams类型注解位置的那个对 FastAPI没有任何特殊含义。FastAPI 不会用它做数据转换、校验等工作那由Depends(CommonQueryParams)负责。因此你甚至可以只写类型为Any的版本见 tutorial003_an_py310.pyfrom typing import Annotated, Any from fastapi import Depends, FastAPI app FastAPI() fake_items_db [{item_name: Foo}, {item_name: Bar}, {item_name: Baz}] class CommonQueryParams: def __init__(self, q: str | None None, skip: int 0, limit: int 100): self.q q self.skip skip self.limit limit app.get(/items/) async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]): response {} if commons.q: response.update({q: commons.q}) items fake_items_db[commons.skip : commons.skip commons.limit] response.update({items: items}) return response非Annotated版本对应commons Depends(CommonQueryParams)。但仍然强烈建议声明类型——这样做你的编辑器才能知道传入参数commons的是什么进而给你提供代码补全与类型检查快捷方式Depends()免重复声明上面的写法仍然有重复代码CommonQueryParams要手写两遍。FastAPI 为这类依赖特指某个将被调用并创建实例的类的情况提供了一种快捷方式。与其写commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]不如写commons: Annotated[CommonQueryParams, Depends()]即把依赖声明为参数类型本身并用不带任何参数的Depends()FastAPI 会从类型注解推断出该实例化哪个类。非Annotated版本对应commons: CommonQueryParams Depends()。完整示例见 tutorial004_an_py310.pyfrom typing import Annotated from fastapi import Depends, FastAPI app FastAPI() fake_items_db [{item_name: Foo}, {item_name: Bar}, {item_name: Baz}] class CommonQueryParams: def __init__(self, q: str | None None, skip: int 0, limit: int 100): self.q q self.skip skip self.limit limit app.get(/items/) async def read_items(commons: Annotated[CommonQueryParams, Depends()]): response {} if commons.q: response.update({q: commons.q}) items fake_items_db[commons.skip : commons.skip commons.limit] response.update({items: items}) return response如果这个技巧让你觉得反而更绕那完全可以忽略它——它不是必须的只是一种用来减少代码重复的简写。需要注意它的适用边界只有当依赖是FastAPI 将会调用它来创建该类实例本身的具体类时Depends()无参写法才成立。若依赖是函数、或者需要在Depends(...)里传入其它对象/参数仍然需要显式写出依赖体。运行验证三种写法行为完全一致本仓库的官方测试覆盖了这三种写法的等价性。在 tests/test_tutorial/test_dependencies/test_tutorial002_tutorial003_tutorial004.py 中tutorial002、tutorial003与tutorial004含_py310与_an_py310变体被统一收集为一个参数化的clientfixture然后对同一组请求断言完全相同的响应。例如请求/items无参数返回完整列表Foo、Bar、Baz请求/items?qfoo在响应中附带q: foo请求/items?qfooskip1从第二项开始返回切片请求/items?qbarlimit2只取前两条可在该文件后续部分看到。测试文件第 10-25 行通过pytest的参数化 fixture 把这 6 个模块tutorial002_py310、tutorial002_an_py310、tutorial003_py310、tutorial003_an_py310、tutorial004_py310、tutorial004_an_py310放进同一套断言从测试层面直接印证函数式 dict 依赖、Annotated[Any, Depends(类)]以及Depends()简写解析出的查询参数与响应结构完全一致。小结围绕类作为依赖这一主题可以提炼出四条可直接使用的经验依赖的本质是 callable。FastAPI 只关心能不能调用以及参数是什么函数、类乃至任意实现了__call__的对象都能成为依赖。类的__init__参数就是依赖参数。把公共查询参数或其它注入数据放进__init__FastAPI 会在每次请求时实例化该类并完成参数解析、转换、校验与 OpenAPI 文档化。类型注解 ≠ 依赖体。Depends(...)中传入的对象才决定依赖逻辑类型注解仅供编辑器与静态检查使用因此应尽量声明真实类型而非Any。Depends()是类即依赖的简写。当依赖正是那个要被调用实例化的类时可以省去在Depends里重复类名但若依赖是函数等其它 callable仍需显式书写。相关文档与源码可继续查阅依赖注入教程目录、本页依赖的核心解析实现 fastapi/dependencies/utils.py 以及等价性测试 test_tutorial002_tutorial003_tutorial004.py。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

数据结构队列:从排队打饭到消息中间件的核心逻辑

数据结构队列:从排队打饭到消息中间件的核心逻辑

数据结构:队列,从排队打饭到消息中间件的核心逻辑队列这东西,说简单是真简单,一句话就能讲完:先进先出。但你要是只把它当成一个“排队”概念,那就亏大了。我这些年看过的代码里,凡是涉及到系统…

📅 2026/10/6 3:57:11
猫抓:浏览器资源嗅探扩展,把网页视频音频一键拿下的完整指南

猫抓:浏览器资源嗅探扩展,把网页视频音频一键拿下的完整指南

猫抓:浏览器资源嗅探扩展,把网页视频音频一键拿下的完整指南 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 看网课想保存&…

📅 2026/9/9 5:12:09
【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的工业环境噪声监测报警系统设计 基于 STM32 或 51 单片机的噪声超限记录与按键操作系统设计(025706)

【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的工业环境噪声监测报警系统设计 基于 STM32 或 51 单片机的噪声超限记录与按键操作系统设计(025706)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

📅 2026/9/22 16:00:32
MORE NEWS

更多资讯

📰

SAP BTP中使用OAuth 2.0实现安全系统通信

1. 项目背景与核心价值 在SAP Business Technology Platform (BTP)环境中实现系统间安全通信是企业级应用开发的关键需求。最近我在一个跨系统数据同步项目中,需要从外部应用通过OData服务访问SAP Cloud Integration(CPI)的消息监控数据。传统…

📰

Multi-Agent系统:重构工作流程的智能协作技术

1. Multi-Agent系统如何重构工作范式 当我在2023年首次尝试用AutoGen框架协调三个AI代理完成市场分析报告时,原本需要8小时的手工工作被压缩到47分钟。这不仅仅是效率的提升——整个工作流程发生了质的变化:数据分析师代理自动清洗原始数据,研…

📰

RFID技术在智能仓储管理中的应用与优化

1. RFID技术如何重构现代仓储管理在传统仓储作业中,工人需要手动扫描每个货物的条形码,平均每个托盘处理时间长达3分钟。而采用RFID技术后,一整车货物通过闸门时就能完成批量识别,整个过程缩短到15秒以内。这种效率提升正是我们开…

📰

基于虚拟化与EPT的内核调试器:Ring0断点原理与工程实践

简介:面向操作系统内核研究与驱动开发者的 Ring0 级调试工具源码包,围绕 Hyperdbg 这一内核调试器展开,适合已具备 x86 保护模式和内核基础知识、希望深入理解调试器实现与系统级问题定位的读者。资源共 93 个文件,以 C 头文件、C…

📰

解决IntelliJ IDEA中Spring Boot项目命令行过长问题

1. 问题现象与背景解析最近在IntelliJ IDEA中运行一个Spring Boot项目时,突然遇到了"Command line is too long"的错误提示。这个报错通常发生在Windows系统下,当项目的类路径(classpath)过长时,IDEA生成的命令行参数超过了Windows…

📰

小程序收货地址解析:云函数代理与三级联动回填实战

简介:面向微信小程序开发者及电商、物流等需要处理收货地址的场景,这套示例聚焦非标准地址的智能标准化:借助腾讯云地址解析API,把用户输入的自然语言地址拆解为省、市、区等结构信息,涵盖API接入、HMAC-SHA1签名、Bas…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬