带有覆盖 Pydantic 内置函数的自定义列表

问题描述 投票:0回答:1

我想实现一个自定义列表。基本上,它应该像普通列表一样运行,除非使用

__contains__
函数。如果列表为空,我们认为它包含所有内容。

然后我想在继承自 BaseModel 的 pydantic 模型中使用这个自定义列表。

该类的实现如下:

class ListAllIfEmpty(list): 
    """
    Normal list. If the list is empty we will consider it contains
    everything
    """
    def __contains__(self, item): 
        if not self: 
            return True
        else: 
            return super().__contains__(item)

当我尝试将其用作 BaseModel 中的属性并为其指定默认值时,我收到有关为

ListAllIfEmpty
生成架构的错误。我收到将
arbitrary_types_allowed
设置为 true 的建议。如果我这样做,pydantic 将停止对对象进行验证。尽管如此,我还是希望 Pydantic 使用普通的 python 内置列表来完成所有检查。具有
ListAllIfEmpty
类型属性的类如下所示:

class A(BaseModel):
    value1: str
    value2: ListAllIfEmpty[int] = Field(default_factory=lambda: [])

错误信息如下:

E   pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for [...].ListAllIfEmpty[typing.Annotated[datetime.date, BeforeValidator(func=<function validate_date at 0x000001EB701040E0>), PlainSerializer(func=<function format_date at 0x000001EB701053A0>, return_type=PydanticUndefined, when_used='unless-none')]]. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.
E
E   If you got this error by calling handler(<some type>) within `__get_pydantic_core_schema__` then you likely need to call `handler.generate_schema(<some type>)` since we do not call `__get_pydantic_core_schema__` on `<some type>` otherwise to avoid infinite recursion.

我发现使用 RootModel 可能会有所帮助。尽管如此,我还没有从文档中完全理解它是如何工作的。我也看到一些人提到“Annotated”,但我只看到人们用它来添加验证信息。我还想保持“ListAllIfEmpty”的行为与普通列表相同,也就是说,能够像

a = [1,2,3]
那样实例化它。

python pydantic
1个回答
0
投票

您应该使

ListAllIfEmpty
继承
RootModel
,并将
root
设置为您想要的任何类型。
之后,您可以自定义您的
__contains__
或其他
root
方法。

class ListAllIfEmpty(RootModel): 
    """
    Normal list. If the list is empty we will consider it contains everything
    """
    root: list[int] = Field(default_factory=list)
    def __contains__(self, item):
        if not self.root:
            return True
        else: 
            return item in self.root
    
    def __getitem__(self, item):
        return self.root[item]
    
    def __iter__(self):
        return iter(self.root)


class A(BaseModel):
    value1: str
    value2: ListAllIfEmpty = Field(default_factory=ListAllIfEmpty)

我在这些操作上测试了一下,这是你想要的吗?

a = A(value1="test", value2=ListAllIfEmpty())
print(1 in a.value2) # True

b = A(value1="test", value2=list())
print(1 in b.value2) # True

c = A(value1="test", value2=[1, 2, 3])
print(1 in c.value2) # True
print(4 in c.value2) # False

d = A(value1="test")
print(1 in d.value2) # True

A(value1="test", value2=[1,"2",3]) # Pass
A(value1="test", value2=[1,"bad",3]) # Fail
© www.soinside.com 2019 - 2024. All rights reserved.