繁体   English   中英

如何给 Pydantic 列表字段一个默认值?

[英]How to give a Pydantic list field a default value?

我想创建一个 Pydantic model ,其中有一个列表字段,未初始化的默认值为空列表。 有没有一种惯用的方法来做到这一点?

对于 Python 的内置数据类对象,您可以使用field(default_factory=list) ,但是在我自己的实验中,这似乎可以防止我的 Pydantic 模型被腌制。 一个天真的实现可能是这样的:

from pydantic import BaseModel

class Foo(BaseModel):
    defaulted_list_field: Sequence[str] = [] # Bad!

但是我们都知道不要使用像空列表文字这样的可变值作为默认值。

那么给 Pydantic 列表字段一个默认值的正确方法是什么?

对于 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=[]

它将被正确处理(深度复制),并且每个模型实例都有自己的空列表。


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)

在查看我同事的合并请求时,我看到使用可变 object 作为默认参数并指出了这一点。 令我惊讶的是,它就像对 object 进行了深度复制一样。 我在项目的自述文件中找到了一个示例,但没有任何说明。 并且突然意识到开发者一直在忽略这个问题很长一段时间(见底部的链接)。

事实上,你可以写这样的东西。 并期望正确的行为:

from pydantic import BaseModel

class Foo(BaseModel):
    defaulted_list_field: List[str] = []

但是引擎盖下会发生什么? 我们需要更深入地了解 go...

在快速搜索源代码后,我发现了这个

class ModelField(Representation):
    ...
    def get_default(self) -> Any:
        return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory()

虽然smart_deepcopy function 是:

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

开放讨论的链接(目前没有答案):

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM