依赖注入是 FastAPI 的核心特性之一,它能极大提升代码的复用性与可维护性。很多人初学 FastAPI 依赖注入,可能只接触了最基本的函数依赖。实际上,FastAPI 的依赖项不仅限于此。本文将带你系统性地掌握 FastAPI 依赖项的两种类型(函数与类)以及两种作用域(请求级与应用级),配合代码示例,帮你构建更优雅、高效的 Web 服务。
先补个小知识:类和实例
在深入讲解依赖类型之前,我们先快速回顾一下 Python 中类和实例的基本概念。
类和实例的知识卡片

简而言之,类定义了对象的模板(属性和方法),而实例是根据这个模板创建的具体对象,每个实例拥有自己独立的状态。
第一种类型:函数作为依赖项
函数依赖是最常见、最直接的用法。它非常简单:每次请求时,FastAPI 都会重新执行一遍这个函数。
来看一个具体的代码示例:
from fastapi import FastAPI, Depends
app = FastAPI()
def get_query_params(
q: str = None,
limit: int = 10
):
return {"q": q, "limit": limit}
@app.get("/items/")
async def read_items(
params: dict =
Depends(get_query_params)
):
return params
上面例子中的 get_query_params 就是一个函数依赖。它的特点是“无状态”,每次调用都是全新的开始。

第二种类型:类作为依赖项
类依赖比函数依赖更强大,因为它可以保存状态,适用于更复杂的业务场景。FastAPI 支持两种类依赖的写法。
写法一:直接用类
我们可以直接将一个类声明为依赖项,FastAPI 会自动实例化它。
from fastapi import FastAPI, Depends
app = FastAPI()
class QueryParams:
def __init__(self, q: str = None,
limit: int = 10):
self.q = q
self.limit = limit
@app.get("/items/")
async def read_items(params:
QueryParams = Depends(QueryParams)):
return {
"q": params.q,
"limit": params.limit
}
写法二:用 __call__ 方法
通过定义 __call__ 方法,我们可以让类的实例本身变得可调用,从而作为依赖项。
from fastapi import FastAPI, Depends
app = FastAPI()
class Counter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
counter = Counter()
@app.get("/count/")
async def get_count(
current_count: int = Depends(counter)
):
return {"count": current_count}
思考一下:为什么这个例子中,每次调用 /count/ 接口,返回的数字都会累计+1?
答案是,我们在外部创建了一个 Counter 类的实例 counter,并将其作为依赖项。由于这个实例在整个应用生命周期内是唯一的,其内部的 count 状态得以保留。

如何选择函数依赖还是类依赖?关键在于你是否需要保存状态。这里有一个核心口诀可以帮助你决策:

两种作用域:请求级 vs 应用级
作用域决定了依赖项实例的生命周期。我们可以用一个简单的比喻来理解:
- 请求级作用域 = 酒店房间里的一次性牙刷毛巾。每次新客人入住(每次请求),都会提供全新的一套。
- 应用级作用域 = 酒店大堂的沙发和电梯。整个酒店只有一份,所有客人共用。
请求级作用域(默认)
这是 FastAPI 的默认行为。每次请求到来时,依赖项都会被重新创建和初始化。
from fastapi import FastAPI, Depends
app = FastAPI()
class RequestCounter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
@app.get("/request-count/")
async def get_request_count(
count: int = Depends(
RequestCounter)):
return {"count": count}
运行上面的代码,你会发现每次请求 /request-count/ 返回的都是 1。因为每次请求 FastAPI 都创建了一个新的 RequestCounter 实例,计数器每次都从0开始加1。
应用级作用域
有些资源(如数据库连接池、全局配置)我们希望在应用整个生命周期内只初始化一次,所有请求共享。这就要用到应用级作用域。
方法一:用 lru_cache 装饰类
利用 functools.lru_cache 装饰器可以缓存类的实例。
from fastapi import FastAPI, Depends
from functools import lru_cache
app = FastAPI()
@lru_cache()
class AppCounter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
@app.get("/app-count/")
async def get_app_count(
count: int =
Depends(AppCounter)):
return {"count": count}
方法二:在外部创建实例
更直接的方式是在模块级别预先创建好实例。
from fastapi import FastAPI, Depends
app = FastAPI()
class AppCounter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
app_counter = AppCounter()
@app.get("/app-count/")
async def get_app_count(
count: int =
Depends(app_counter)):
return {"count": count}
使用以上两种方法,每次请求 /app-count/ 返回的数字都会在上一次的基础上递增。这是因为所有请求都依赖于同一个 AppCounter (或 app_counter) 实例。
依赖项的嵌套调用
依赖项可以像搭积木一样层层嵌套,FastAPI 会自动解析并按照正确的顺序执行它们。这在实际开发中非常有用,例如构建一个需要验证 API Key、获取用户信息、再建立数据库连接的接口。
from fastapi import FastAPI, Depends,
HTTPException
app = FastAPI()
def get_api_key(api_key: str):
if api_key != "secret":
raise HTTPException(status_code=401,
detail="Invalid API Key")
return api_key
def get_current_user(api_key: str
= Depends(get_api_key)):
return {"username": "admin",
"api_key": api_key)
def get_db_connection(current_user:
dict = Depends(get_current_user)):
return {"db": "connection",
"user": current_user}
@app.get("/protected/")
async def protected_route(db =
Depends(get_db_connection)):
return {"message": "You are in!", "db": db}
依赖链解析顺序:
FastAPI 会先解析 get_db_connection,发现它依赖 get_current_user;进而发现 get_current_user 又依赖 get_api_key。最终的执行顺序是:get_api_key -> get_current_user -> get_db_connection -> protected_route。

常见误区与避坑指南
❌ 误区一:在类依赖中错误使用实例变量
如果你希望类依赖保存状态,但写法上却导致每次请求都创建新类,状态将无法保留。
错误写法:
@app.get("/count/")
async def get_count(count: int
= Depends(Counter)):
...
这种写法中,Depends(Counter) 会导致每个请求都新建一个 Counter 类,count 无法累加。
正确写法:
counter = Counter()
@app.get("/count/")
async def get_count(count:
int = Depends(counter)):
预先创建实例 counter 并传入 Depends,才能保证状态持久化。
❌ 误区二:过度使用应用级作用域
不是所有依赖都适合做成应用级。像数据库连接池、应用配置这类开销大、且无请求特异性的对象,适合应用级作用域。而用户身份、请求参数、事务上下文等与具体请求强相关的对象,必须使用请求级作用域,否则会导致数据混乱。
❌ 误区三:依赖链过长
虽然依赖可以嵌套,但应避免创建过长的依赖链(例如超过3层)。过长的依赖链会降低代码可读性,增加调试难度,形成“依赖地狱”。当逻辑变得复杂时,应考虑将部分依赖拆分或重构。
总结
掌握 FastAPI 依赖项的类型与作用域,是写出整洁、高效后端代码的关键。我们来回顾一下今天的核心要点:
- 函数依赖:简单直接,无状态,适合参数提取、Token验证等一次性操作。
- 类依赖:可以保存状态,适合需要多个方法协作或管理内部状态的复杂业务逻辑,这其实是一种常用的代码组织设计模式。
- 请求级作用域:默认行为,每次请求重新创建依赖实例,保证请求间的隔离性。
- 应用级作用域:依赖实例全局唯一,适合初始化开销大、可共享的资源,如配置、连接池。
- 嵌套依赖:允许构建清晰的依赖链,FastAPI 会自动管理解析与执行顺序。
简单来说,你可以这样记忆:类依赖像一位有记忆的服务员,记得你之前的喜好;函数依赖则像每次都是新来的服务员,一切从零开始。 合理运用它们,能让你的 FastAPI 项目结构更清晰,维护性更高。在云栈社区,你可以找到更多关于微服务架构与后端开发的深度讨论和实战案例,与广大开发者一起交流成长。