V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
mkroen
V2EX  ›  Python

请教: pydantic 对 BaseModel 使用的 bug?

  •  
  •   mkroen · 27 天前 · 1342 次点击

    今天在项目中使用 BaseModel 遇到了一个问题,model 的某个字段的值一直赋值不上,在经过一番研究之后,我将其简化,发出来和大伙们讨论


    from pydantic import BaseModel
    
    
    class A(BaseModel):
        data: dict | list | BaseModel
    
    
    class B(BaseModel):
        data: BaseModel | dict | list
    
    
    a1 = A(data={"a": 1, "b": 2})
    a2 = A(data=a1)
    a3 = A(data=["1", "2"])
    
    b1 = B(data={"a": 1, "b": 2})
    b2 = B(data=b1)
    b3 = B(data=["1", "2"])
    
    print(a1)  # "data={'a': 1, 'b': 2}"            正常
    print(a2)  # "data=A(data={'a': 1, 'b': 2})"    正常
    print(a3)  # "data=['1', '2']"                  正常
    
    print(b1)  # "data=BaseModel()"                 不正常
    print(b2)  # "data=B(data=BaseModel())"         正常
    print(b3)  # "data=['1', '2']"                  正常
    

    这里可以看到对于 b1 的实例化,data 的值并没有成功赋值给 b1.data
    按理来说,不管我做不做类型注解,这里都不应该影响我正常赋值和实例化


    环境:
    python3.10.8
    pydantic==2.6.3


    当我把 pydantic 更新到最新的 2.10.2 时,实例化 b1 会报错,但是 b3 依然可以执行

    AttributeError: 'BaseModel' object has no attribute '__private_attributes__'
    

    我有尝试去 pydantic 的 issue 搜索过,但相关 issue 太多了,没找到相似的。
    感觉像是 pydantic 的 bug ?还是因为有什么特性?

    14 条回复    2024-12-04 15:46:19 +08:00
    Lychee0
        1
    Lychee0  
       27 天前   ❤️ 1
    ```python
    class B(BaseModel):
    data: Union[Self, Dict, List]
    ```
    先这么写倒是可以,mypy & ruff 扫了下能过

    我发现不标 Self 时 b2.data 还会变成 List ,好奇怪
    Lychee0
        2
    Lychee0  
       27 天前
    自动转 List 应该是特性?(这个不应该抛错误吗
    mkroen
        3
    mkroen  
    OP
       27 天前
    @Lychee0 #1
    python3.10 还没有 typing.Self ,这里我写 BaseModel 意思是,data 可以是其他继承 BaseModel 的 class ,而不一定仅是 B 这个 class
    jzhouwyy
        4
    jzhouwyy  
       27 天前
    @Lychee0 #1 python3.12 pydantic2.10.2 按你这样写啥事没有
    sunfkny
        5
    sunfkny  
       26 天前   ❤️ 1
    缺少用于显示的 __private_attributes__ 和 __pydantic_computed_fields__,空继承一下或者补上属性就好了,正常继承,metaclass 会设置这两个属性的

    class BaseModel(BaseModel): pass

    BaseModel.__private_attributes__ = {}
    BaseModel.__pydantic_computed_fields__ = {}
    chaunceywe
        6
    chaunceywe  
       26 天前   ❤️ 1
    这种最好用 union+discriminator,没 type pydantic 也不知道到底按哪个类型解析
    009694
        7
    009694  
       25 天前   ❤️ 2
    因为 pydantic 是按照类型实例化是否成功决定是否匹配上的啊。 你的 b 按照顺序会先尝试使用 BaseModel 进行实例化 结果还真成功了 自然就成 BaseModel 了
    009694
        8
    009694  
       25 天前   ❤️ 2
    BaseModel 的包含范围和 dict 是一致的 虽然实例化的时候传进去的东西会全丢失 但是不妨碍外部认为转换成功了
    mkroen
        9
    mkroen  
    OP
       24 天前
    @sunfkny 这样能解决 2.10.2 的报错问题,但是 b1 的实例化还是没接收到参数。我的本意是,data 为任意继承 basemodel 的类都满足
    mkroen
        10
    mkroen  
    OP
       24 天前
    @009694 #7 感谢解释。但是这里我觉得 pydantic 直接把 BaseModel 认为和 dict 一致,有点反直觉。如果 data 注解为仅 BaseModel 类型,当我传入一个 dict 类型,pylance 是会标红的:无法将“dict[str, int]”类型的参数分配给函数“__init__”中类型为“BaseModel”的参数“data”。
    mkroen
        11
    mkroen  
    OP
       24 天前
    @009694 #7 pylance 会识别他标注过的几种类型,这一点我理解。但感觉还是像楼上说的用 Union 解决好一点。
    009694
        12
    009694  
       24 天前 via iPhone
    @mkroen union 和|是完全一致的 问题不出在这里。
    009694
        13
    009694  
       24 天前   ❤️ 1
    @mkroen 你需要的是让 union 中的类型不要有可解析的交集 否则你永远不知道会解析成谁 一旦 pydantic 内部解析优先级变了 那你的代码就炸了。BaseModel 和 dict 出现交集这个事我理解算一种历史遗留产物,猜测是是当初 pydantic 诞生是为了实现一种 typeddict , 所以在传入参数是 dict 且指定类型是 BaseModel 的时候会尝试解析成 BaseModel 。
    mkroen
        14
    mkroen  
    OP
       22 天前
    @009694 #13 感谢。我换种写法已经解决了问题,主要是想明白这里的错误的原因。这种类似子集的类型,把子集放前面,超集放后面就可以解决了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3383 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 11:24 · PVG 19:24 · LAX 03:24 · JFK 06:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.