使用Pydantic与typing.Protocol时遇架构生成错误,求解决
解决Pydantic与typing.Protocol联用的Schema生成错误
错误原因
Pydantic默认无法为typing.Protocol类型生成核心schema,因为Protocol是用于静态类型检查的结构契约,它定义的是对象需要满足的接口,而非可直接序列化/反序列化的具体类型。Pydantic无法自动推断这类抽象类型的序列化规则,因此抛出PydanticSchemaGenerationError。
解决方案
方案1:启用arbitrary_types_allowed(快速解决)
通过模型配置允许任意类型,让Pydantic跳过对Protocol字段的schema生成,转而依赖@runtime_checkable装饰器提供的运行时类型检查。
from typing import TypeVar, Protocol, Generic, runtime_checkable from pydantic import BaseModel D = TypeVar('D') @runtime_checkable class SupportsDimensionality(Protocol[D]): dimensionality: D class ValueModel(BaseModel, Generic[D]): value: SupportsDimensionality[D] model_config = {"arbitrary_types_allowed": True}
注意:此方法的局限性是Pydantic不会自动处理value字段的序列化/反序列化,需要确保传入的对象本身可被序列化,或自行实现序列化逻辑。
方案2:为Protocol实现自定义Pydantic Schema(完全支持)
如果需要Pydantic完全支持Protocol的验证与序列化,可以实现__get_pydantic_core_schema__方法,手动定义schema规则。
from typing import TypeVar, Protocol, Generic, runtime_checkable, Any from pydantic import BaseModel, GetCoreSchemaHandler from pydantic_core import core_schema D = TypeVar('D') @runtime_checkable class SupportsDimensionality(Protocol[D]): dimensionality: D @classmethod def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: # 处理泛型参数D的schema d_schema = handler(D) # 验证规则:检查对象符合Protocol契约 validate_rule = core_schema.is_instance_of(cls) # 序列化规则:提取dimensionality字段 serialize_rule = core_schema.plain_serializer_function_ser_schema( lambda obj: {"dimensionality": obj.dimensionality} ) # 支持从字典反序列化生成符合Protocol的对象 deserialize_rule = core_schema.general_plain_validator_function( lambda value, info: type('TempDimensional', (), {"dimensionality": value['dimensionality']})() if isinstance(value, dict) else value ) return core_schema.union_schema( [validate_rule, deserialize_rule], serialization=serialize_rule ) class ValueModel(BaseModel, Generic[D]): value: SupportsDimensionality[D]
说明:这个方案让Pydantic完全理解Protocol的结构,既能验证输入是否符合契约,也能正确完成序列化/反序列化,但需要熟悉Pydantic Core的schema系统。
方案3:用具体Pydantic模型结合Protocol(推荐)
如果你的Protocol实现类本身是Pydantic模型,可以直接定义一个基础模型来实现Protocol契约,这样既满足静态类型检查,又能让Pydantic自动生成schema。
from typing import TypeVar, Protocol, Generic, runtime_checkable from pydantic import BaseModel D = TypeVar('D') @runtime_checkable class SupportsDimensionality(Protocol[D]): dimensionality: D # 实现Protocol的具体Pydantic模型 class DimensionalBaseModel(BaseModel, Generic[D]): dimensionality: D class ValueModel(BaseModel, Generic[D]): value: DimensionalBaseModel[D]
优势:这种方式完全贴合Pydantic的设计思路,无需额外配置,同时保留了Protocol带来的静态类型检查能力,是最常用的实践方案。
内容的提问来源于stack exchange,提问作者Rick
相关产品推荐
相关产品推荐

