FastAPI:如何在类可调用依赖中缓存依赖返回值?
如何在FastAPI的类形式可调用依赖中传参并实现跨端点的返回值缓存?
问题场景与现象
你编写了如下FastAPI示例代码,期望相同参数的类依赖能跨端点共享缓存结果:
# run.py from functools import cache import uvicorn from fastapi import FastAPI, Depends app = FastAPI() class Dep: def __init__(self, inp): self.inp = inp @cache def __call__(self): print(self.inp * 100) return self.inp @app.get("/a") def a(v: str = Depends(Dep("a"))): return v @app.get("/a/a") def aa(v: str = Depends(Dep("a"))): return v @app.get("/b") def b(v: str = Depends(Dep("b"))): return v @app.get("/b/b") def bb(v: str = Depends(Dep("b"))): return v def main(): uvicorn.run( "run:app", host="0.0.0.0", reload=True, port=8000, workers=1 ) if __name__ == "__main__": main()
预期效果:
- 首次访问
/a或/a/a时,控制台打印100个"a",后续访问不再打印 - 首次访问
/b或/b/b时,控制台打印100个"b",后续访问不再打印
实际现象:
每个端点首次访问都会打印对应日志,只有同一端点的后续访问才会触发缓存。原因是每次调用Dep("a")或Dep("b")都会创建全新的类实例,每个实例的__call__方法缓存相互独立,而FastAPI自带的依赖缓存仅作用于同一端点的重复调用。
类形式依赖的解决方案
要实现类依赖传参且跨端点共享缓存,核心是让相同参数的Dep实例复用,而非每次新建。以下是两种可行方案:
方案一:通过类方法+缓存管理实例
# run.py from functools import cache import uvicorn from fastapi import FastAPI, Depends app = FastAPI() class Dep: # 缓存类实例,相同inp参数返回同一个实例 @classmethod @cache def get_instance(cls, inp): return cls(inp) def __init__(self, inp): self.inp = inp @cache def __call__(self): print(self.inp * 100) return self.inp @app.get("/a") def a(v: str = Depends(Dep.get_instance("a"))): return v @app.get("/a/a") def aa(v: str = Depends(Dep.get_instance("a"))): return v @app.get("/b") def b(v: str = Depends(Dep.get_instance("b"))): return v @app.get("/b/b") def bb(v: str = Depends(Dep.get_instance("b"))): return v def main(): uvicorn.run( "run:app", host="0.0.0.0", reload=True, port=8000, workers=1 ) if __name__ == "__main__": main()
原理:通过get_instance类方法结合@cache,确保相同inp参数始终返回同一个Dep实例,该实例的__call__方法缓存即可跨端点生效。
方案二:重载__new__方法实现单例缓存
# run.py from functools import cache import uvicorn from fastapi import FastAPI, Depends app = FastAPI() class Dep: _instances = {} def __new__(cls, inp): # 相同inp返回已存在的实例 if inp not in cls._instances: cls._instances[inp] = super().__new__(cls) return cls._instances[inp] def __init__(self, inp): # 避免重复初始化已存在的实例 if not hasattr(self, 'inp'): self.inp = inp @cache def __call__(self): print(self.inp * 100) return self.inp @app.get("/a") def a(v: str = Depends(Dep("a"))): return v @app.get("/a/a") def aa(v: str = Depends(Dep("a"))): return v @app.get("/b") def b(v: str = Depends(Dep("b"))): return v @app.get("/b/b") def bb(v: str = Depends(Dep("b"))): return v def main(): uvicorn.run( "run:app", host="0.0.0.0", reload=True, port=8000, workers=1 ) if __name__ == "__main__": main()
原理:通过__new__方法控制实例创建逻辑,相同inp直接返回已缓存的实例,确保Dep("a")每次调用都得到同一个对象,进而让__call__的缓存跨端点共享。
对比函数式方案
你实现的函数式依赖方案是可行的,通过外层@cache的dep函数返回同一个sub_dep函数,确保相同inp对应的依赖函数复用。类方案的优势在于当依赖需要维护更多状态或方法时,结构会更清晰易扩展。
内容的提问来源于stack exchange,提问作者Amin Ba
相关产品推荐
相关产品推荐

