![](/img/trans.png)
[英]How change .dict behavior in Pydantic for including an extra deep field
[英]Why does the pydantic dataclass cast a list to a dict? How to prevent this behavior?
我對 pydantic 數據類的行為有點困惑。 為什么dict
類型接受dict
list
作為有效dict
,為什么將其轉換為鍵dict
? 難道我做錯了什么? 這是某種有意為之的行為嗎?如果是,是否有辦法阻止這種行為?
代碼示例:
from pydantic.dataclasses import dataclass
@dataclass
class X:
y: dict
print(X([{'a':'b', 'c':'d'}]))
Output:
X(y={'a': 'c'})
哈,這其實有點有趣。 忍受我...
為什么
dict
類型接受dict
list
作為有效dict
,為什么將其轉換為鍵dict
?
當我們仔細查看 Pydantic 模型使用的默認dict_validator
時,就可以解釋這一點。 它所做的第一件事(對任何非dict
)是試圖將值強制轉換為dict
。
用你的具體例子試試這個:
y = [{'a': 'b', 'c': 'd'}]
assert dict(y) == {'a': 'c'}
這是為什么?
好吧,要初始化dict
,您可以傳遞不同種類的 arguments。一種選擇是傳遞一些Iterable
和
iterable 中的每個項目本身必須是一個恰好有兩個對象的 iterable。 每個項目的第一個 object 成為新字典中的鍵,第二個 object 成為對應的值。
在您的示例中,您恰好有一個可迭代對象(特別是list
),並且該可迭代對象中唯一的項目本身就是一個可迭代對象(特別是dict
)。 字典是如何默認迭代的? 通過他們的鑰匙! 由於該字典{'a': 'b', 'c': 'd'}
恰好有兩個鍵值對,這意味着當迭代它時會產生這兩個鍵,即"a"
和"c"
:
d = {'a': 'b', 'c': 'd'}
assert tuple(iter(d)) == ('a', 'c')
正是這種機制允許從 2 元組列表中構造dict
,例如:
data = [('a', 1), ('b', 2)]
assert dict(data) == {'a': 1, 'b': 2}
在您的情況下,這會導致您顯示的結果,乍一看似乎很奇怪且出乎意料,但當您考慮字典初始化的邏輯時,實際上是有道理的。
有趣的是,這僅在list
中的dict
恰好有兩個鍵值對時才有效。 多一點或少一點都會導致錯誤。 (自己試試吧。)
簡而言之:這種行為既不是 Pydantic 也不是數據類的特殊行為,而是常規dict
初始化的結果。
難道我做錯了什么?
我會說,是的。 您嘗試分配給Xy
的值是一個list
,但您聲明它是一個dict
。 所以這顯然是錯誤的。 我知道有時數據來自外部來源,所以這可能不取決於你。
這是某種有意為之的行為嗎[...]?
這是一個很好的問題,因為我很想知道 Pydantic 團隊是否知道這種邊緣情況及其導致的奇怪結果。 我會說,字典驗證器的實現方式至少是可以理解的。
有沒有辦法防止這種行為?
是的。 除了不在那里傳遞列表的明顯解決方案之外。
您可以添加自己的自定義驗證器,將其配置為pre=True
並讓它例如只允許實際直接的dict
實例繼續進行進一步驗證。 然后你可以立即捕獲這個錯誤。
希望這可以幫助。
感謝您對此有所啟發,因為一開始這也會讓我失望。 我想我會開始深入研究 Pydantic 問題跟蹤器和 PR,看看是否可以/應該/將以某種方式解決這個問題。
這是前面提到的“嚴格”驗證器的非常簡單的實現,它可以防止dict
-coercion 並立即為非dict
引發錯誤:
from typing import Any
from pydantic.class_validators import validator
from pydantic.dataclasses import dataclass
from pydantic.fields import ModelField, SHAPE_DICT, SHAPE_SINGLETON
@dataclass
class X:
y: dict
@validator("*", pre=True)
def strict_dict(cls, v: Any, field: ModelField) -> Any:
declared_dict_type = (
field.type_ is dict and field.shape is SHAPE_SINGLETON
or field.shape is SHAPE_DICT
)
if declared_dict_type and not isinstance(v, dict):
raise TypeError(f"value must be a `dict`, got {type(v)}")
return v
if __name__ == '__main__':
print(X([{'a': 'b', 'c': 'd'}]))
Output:
Traceback (most recent call last):
File "....py", line 24, in <module>
print(X([{'a': 'b', 'c': 'd'}]))
File "pydantic/dataclasses.py", line 313, in pydantic.dataclasses._add_pydantic_validation_attributes.new_init
File "pydantic/dataclasses.py", line 416, in pydantic.dataclasses._dataclass_validate_values
# worries about external callers.
pydantic.error_wrappers.ValidationError: 1 validation error for X
y
value must be a `dict`, got <class 'list'> (type=type_error)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.