Sal*_*ley 11 python python-dataclasses pydantic fastapi
我想创建一个 Pydantic 模型,其中有一个列表字段,未初始化的默认值为空列表。有没有一种惯用的方法来做到这一点?
对于 Python 的内置数据类对象,您可以使用field(default_factory=list),但是在我自己的实验中,这似乎可以防止我的 Pydantic 模型被腌制。一个天真的实现可能是这样的:
from pydantic import BaseModel
class Foo(BaseModel):
defaulted_list_field: Sequence[str] = [] # Bad!
Run Code Online (Sandbox Code Playgroud)
但是我们都知道不要使用像空列表文字这样的可变值作为默认值。
那么给 Pydantic 列表字段一个默认值的正确方法是什么?
ale*_*ame 26
对于 pydantic,您可以使用可变默认值,例如:
class Foo(BaseModel):
defaulted_list_field: List[str] = []
f1, f2 = Foo(), Foo()
f1.defaulted_list_field.append("hey!")
print(f1) # defaulted_list_field=['hey!']
print(f2) # defaulted_list_field=[]
Run Code Online (Sandbox Code Playgroud)
它将被正确处理(深度复制),并且每个模型实例都有自己的空列表。
Pydantic 也有default_factory 参数. 在空列表的情况下,结果将是相同的,而是在声明具有默认值的字段时使用它,您可能希望它是动态的(即每个模型不同)。
from typing import List
from pydantic import BaseModel, Field
from uuid import UUID, uuid4
class Foo(BaseModel):
defaulted_list_field: List[str] = Field(default_factory=list)
uid: UUID = Field(default_factory=uuid4)
Run Code Online (Sandbox Code Playgroud)
fun*_*man 13
在查看同事的合并请求时,我看到使用可变对象作为默认参数并指出了这一点。令我惊讶的是,它的工作原理就像是对对象进行了深度复制。我在项目的自述文件中找到了一个示例,但没有任何说明。突然意识到开发人员长期以来一直忽略这个问题(请参阅底部的链接)。
事实上,你可以写这样的东西。并期望正确的行为:
from pydantic import BaseModel
class Foo(BaseModel):
defaulted_list_field: List[str] = []
Run Code Online (Sandbox Code Playgroud)
但幕后会发生什么呢?我们需要更深入...
快速搜索源代码后,我发现了这一点:
class ModelField(Representation):
...
def get_default(self) -> Any:
return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory()
Run Code Online (Sandbox Code Playgroud)
而smart_deepcopy函数是:
def smart_deepcopy(obj: Obj) -> Obj:
"""
Return type as is for immutable built-in types
Use obj.copy() for built-in empty collections
Use copy.deepcopy() for non-empty collections and unknown objects
"""
obj_type = obj.__class__
if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES:
return obj # fastest case: obj is immutable and not collection therefore will not be copied anyway
try:
if not obj and obj_type in BUILTIN_COLLECTIONS:
# faster way for empty collections, no need to copy its members
return obj if obj_type is tuple else obj.copy() # type: ignore # tuple doesn't have copy method
except (TypeError, ValueError, RuntimeError):
# do we really dare to catch ALL errors? Seems a bit risky
pass
return deepcopy(obj) # slowest way when we actually might need a deepcopy
Run Code Online (Sandbox Code Playgroud)
另外,正如评论中提到的,您不能直接在数据库属性声明中使用可变默认值(而是使用 default_factory )。所以这个例子是无效的:
from pydantic.dataclasses import dataclass
@dataclass
class Foo:
bar: list = []
Run Code Online (Sandbox Code Playgroud)
并给出:
ValueError: mutable default <class 'list'> for field bar is not allowed: use default_factory
Run Code Online (Sandbox Code Playgroud)
公开讨论的链接(到目前为止还没有答案):
| 归档时间: |
|
| 查看次数: |
4513 次 |
| 最近记录: |