You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义ListAllIfEmpty类在Pydantic中使用的Schema生成问题求助

问题描述

我需要实现一个自定义列表类ListAllIfEmpty,继承自Python内置list,仅重写__contains__方法:当列表为空时,判定其包含任意元素;非空时与普通列表行为一致。希望将该类用于Pydantic的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)

在Pydantic模型中的使用方式:

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

运行时出现Schema生成错误:

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.

If you got this error by calling handler(<some type>) within __get_pydantic_core_schema__ then you likely need to call handler.generate_schema(&lt;some type&gt;) since we do not call __get_pydantic_core_schema__ on &lt;some type&gt; otherwise to avoid infinite recursion.

设置arbitrary_types_allowed=True会丢失Pydantic的校验能力,希望保持类像普通列表一样的实例化方式(如a = [1,2,3]),寻求解决方案。

解决方案

要让Pydantic正确识别自定义列表类型并保留校验能力,需要为ListAllIfEmpty实现__get_pydantic_core_schema__类方法,同时让类支持泛型(因为用到了ListAllIfEmpty[int]这种形式)。

完整实现代码

from typing import Any, TypeVar, Generic
from pydantic_core import core_schema
from pydantic import BaseModel, Field

T = TypeVar('T')

class ListAllIfEmpty(list, Generic[T]):
    """
    普通列表,但为空时会判定包含任意元素
    """
    def __contains__(self, item: Any) -> bool:
        if not self:
            return True
        return super().__contains__(item)
    
    @classmethod
    def __get_pydantic_core_schema__(
        cls, source_type: Any, handler: core_schema.SchemaGenerator
    ) -> core_schema.CoreSchema:
        # 获取对应泛型列表的Pydantic核心Schema
        list_schema = handler.generate_schema(list[T])
        
        # 自定义序列化/反序列化逻辑,确保实例化为ListAllIfEmpty
        return core_schema.no_info_wrap_validator_function(
            lambda v, info: cls(v),
            list_schema,
            serialization=core_schema.plain_serializer_function_ser_schema(
                lambda instance: list(instance), return_type=list
            ),
        )

在Pydantic模型中使用

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

关键说明

  • 泛型支持:继承Generic[T]后,ListAllIfEmpty可以像标准List[T]一样指定元素类型,Pydantic会自动对元素执行类型校验。
  • Schema兼容:__get_pydantic_core_schema__方法中,先通过handler.generate_schema(list[T])获取普通列表的校验规则,再将校验后的结果包装为ListAllIfEmpty实例,既保留原生列表的校验能力,又实现自定义类的逻辑。
  • 序列化处理:通过plain_serializer_function_ser_schema确保序列化时将自定义列表转换为普通列表,避免序列化异常。
  • 实例化方式:依然支持类似普通列表的实例化,比如empty_list = ListAllIfEmpty()、num_list = ListAllIfEmpty([1,2,3]),也可以在Pydantic中用default_factory=ListAllIfEmpty设置默认值。

验证测试

# 测试空列表的contains逻辑
empty_list = ListAllIfEmpty[int]()
assert 5 in empty_list
assert "test_str" in empty_list

# 测试非空列表的contains逻辑
non_empty_list = ListAllIfEmpty[int]([1,2,3])
assert 2 in non_empty_list
assert 4 not in non_empty_list

# 测试Pydantic自动校验与类型转换
model = A(value1="demo", value2=["1", 2, 3])
assert model.value2 == ListAllIfEmpty([1,2,3])

# 非法输入会触发校验错误
try:
    A(value1="demo", value2=["not_an_integer"])
except Exception as e:
    print(e)  # 抛出类型校验错误,符合预期

内容的提问来源于stack exchange,提问作者Ana Arduengo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 13:34:54