在FastAPI中使用带__post_init__的嵌套Python数据类遇问题求助
解决方案:FastAPI中触发嵌套原生dataclass的__post_init__
错误原因
FastAPI依赖Pydantic解析请求数据,默认情况下不会自动实例化嵌套的原生Python dataclass,而是将嵌套对象转换为字典。这就导致Material的__post_init__方法从未执行,自然没有生成fd属性,最终Square的__post_init__访问material.fd时抛出AttributeError。
可行方案
方案1:将原生dataclass替换为Pydantic dataclass
如果有权限修改原库的代码,直接用Pydantic的dataclass装饰器替代标准库的,Pydantic会自动处理嵌套类的实例化并触发所有__post_init__方法。
修改后的库代码:
from pydantic.dataclasses import dataclass # 替换标准库的dataclass @dataclass class Material: f: float sf: float = 1.5 def __post_init__(self): self.fd = self.f / self.sf @dataclass class Square: w: float material: Material def __post_init__(self): self.area = self.w * self.w self.force_max = self.material.fd * self.area
API代码无需改动,直接使用:
@app.post("/square/") async def root(square: Square): return { "area": square.area, "force_max": square.force_max, }
方案2:手动实例化嵌套dataclass(不修改原库)
如果无法修改原库,在API层手动将请求中的字典数据转换为对应dataclass实例,确保__post_init__被触发。
@app.post("/square/") async def root(square_data: dict): # 先实例化Material,触发其__post_init__生成fd material = Material(**square_data["material"]) # 再实例化Square,此时material已包含fd属性 square = Square(w=square_data["w"], material=material) return { "area": square.area, "force_max": square.force_max, }
方案3:用Pydantic的parse_obj_as自动解析(不修改原库)
使用Pydantic提供的parse_obj_as工具,自动将请求数据解析为嵌套的原生dataclass实例,无需手动处理嵌套层级。
from pydantic import parse_obj_as @app.post("/square/") async def root(square_data: dict): # parse_obj_as会自动实例化所有嵌套的dataclass并触发__post_init__ square = parse_obj_as(Square, square_data) return { "area": square.area, "force_max": square.force_max, }
验证逻辑
上述方案的核心都是确保Material被正确实例化,使其__post_init__执行并生成fd属性,这样Square的__post_init__就能正常访问该属性计算force_max。
内容的提问来源于stack exchange,提问作者daalgi
相关产品推荐
相关产品推荐

