在审查同事的合并请求时,我发现使用可变对象作为默认参数,并指出了这一点。让我惊讶的是,它的工作方式就好像已经对该对象进行了深层复制一样。我在项目自述文件中找到了一个示例,但没有任何解释。突然间我意识到开发人员长期以来一直忽略了这个问题(请参见底部的链接)。
实际上,你可以编写类似这样的内容, 并期望有正确的行为:
from pydantic import BaseModel
class Foo(BaseModel):
defaulted_list_field: List[str] = []
但是,在底层会发生什么呢?
我们需要更深入一些...
在快速搜索源代码后,我找到了这个:
class ModelField(Representation):
...
def get_default(self) -> Any:
return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory()
在IT技术中,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
此外,正如评论中提到的那样,在数据库属性声明中不能直接使用可变默认值(请使用default_factory代替)。因此,这个例子 是无效的:
from pydantic.dataclasses import dataclass
@dataclass
class Foo:
bar: list = []
并给出:
ValueError: mutable default <class 'list'> for field bar is not allowed: use default_factory
开放讨论的链接 (目前没有答案):
pydantic
非常精心设计的特点。 - kevlarrValueError:mutable default <class 'list'> for field defaulted_list_field is not allowed: use default_factory
。 - luckydonald@dataclass
不允许像标准数据类那样有可变的默认字段。 - alex_nonameList[int] = []
。尽管它没有明确提到处理可变默认参数,但这个示例确实做到了。 - Tim Skov Jacobsen