今天在项目中使用 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 ?还是因为有什么特性?
1
Lychee0 28 天前 1
```python
class B(BaseModel): data: Union[Self, Dict, List] ``` 先这么写倒是可以,mypy & ruff 扫了下能过 我发现不标 Self 时 b2.data 还会变成 List ,好奇怪 |
2
Lychee0 28 天前
自动转 List 应该是特性?(这个不应该抛错误吗
|
3
mkroen OP @Lychee0 #1
python3.10 还没有 typing.Self ,这里我写 BaseModel 意思是,data 可以是其他继承 BaseModel 的 class ,而不一定仅是 B 这个 class |
5
sunfkny 28 天前 1
缺少用于显示的 __private_attributes__ 和 __pydantic_computed_fields__,空继承一下或者补上属性就好了,正常继承,metaclass 会设置这两个属性的
class BaseModel(BaseModel): pass BaseModel.__private_attributes__ = {} BaseModel.__pydantic_computed_fields__ = {} |
6
chaunceywe 27 天前 1
这种最好用 union+discriminator,没 type pydantic 也不知道到底按哪个类型解析
|
7
009694 27 天前 2
因为 pydantic 是按照类型实例化是否成功决定是否匹配上的啊。 你的 b 按照顺序会先尝试使用 BaseModel 进行实例化 结果还真成功了 自然就成 BaseModel 了
|
8
009694 27 天前 2
BaseModel 的包含范围和 dict 是一致的 虽然实例化的时候传进去的东西会全丢失 但是不妨碍外部认为转换成功了
|
10
mkroen OP @009694 #7 感谢解释。但是这里我觉得 pydantic 直接把 BaseModel 认为和 dict 一致,有点反直觉。如果 data 注解为仅 BaseModel 类型,当我传入一个 dict 类型,pylance 是会标红的:无法将“dict[str, int]”类型的参数分配给函数“__init__”中类型为“BaseModel”的参数“data”。
|