如何在Python Mypy中传入类到泛型并实现反序列化?
泛型类
OpenApiResponse的Mypy类型错误及优化方案 问题场景
定义了带deserialize类方法的泛型类OpenApiResponse,代码如下:
@dataclasses.dataclass class OpenApiResponse(typing.Generic[T]): response_cls: typing.Type[T] content: typing.Optional[typing.Dict[str, MediaType]] = None headers: typing.Optional[typing.Dict[str, typing.Type[HeaderParameterWithoutName]]] = None headers_schema: typing.Optional[typing.Type[schemas.DictSchema]] = None @classmethod def deserialize(cls, response: urllib3.HTTPResponse, configuration: schema_configuration_.SchemaConfiguration) -> T: ... return cls.response_cls( response=response, headers=deserialized_headers, body=deserialized_body )
Mypy在return语句处抛出错误:
Access to generic instance variables via class is ambiguous
对应的response_cls实现类:
@dataclasses.dataclass class ApiResponse(api_response.ApiResponse): response: urllib3.HTTPResponse body: application_json_schema.SchemaTuple headers: schemas.Unset = schemas.unset
使用OpenApiResponse的示例代码:
@dataclasses.dataclass class ApiResponse(api_response.ApiResponse): response: urllib3.HTTPResponse body: typing.Union[ application_xml_schema.SchemaTuple, application_json_schema.SchemaTuple, ] headers: schemas.Unset = schemas.unset class SuccessfulXmlAndJsonArrayOfPet(api_client.OpenApiResponse[ApiResponse]): response_cls = ApiResponse class ApplicationXmlMediaType(api_client.MediaType): schema: typing_extensions.TypeAlias = application_xml_schema.Schema class ApplicationJsonMediaType(api_client.MediaType): schema: typing_extensions.TypeAlias = application_json_schema.Schema Content = typing_extensions.TypedDict( 'Content', { 'application/xml': typing.Type[ApplicationXmlMediaType], 'application/json': typing.Type[ApplicationJsonMediaType], } ) content: Content = { 'application/xml': ApplicationXmlMediaType, 'application/json': ApplicationJsonMediaType, }
需求是解决Mypy错误,同时找到更优的反序列化代码结构,优先保留类变量调用deserialize的方式。
解决方案
1. 修复Mypy错误:将response_cls改为类变量
当前response_cls是dataclass的实例字段,但在类方法deserialize中通过cls.response_cls访问,Mypy会判定为访问实例变量导致歧义。只需将response_cls改为类变量,并明确其与泛型参数T的类型关联:
修改后的OpenApiResponse定义:
import typing import dataclasses from typing import Type, Generic, Optional, Dict class OpenApiResponse(typing.Generic[T]): # 定义为类变量,明确类型为Type[T] response_cls: typing.Type[T] content: Optional[Dict[str, MediaType]] = None headers: Optional[Dict[str, Type[HeaderParameterWithoutName]]] = None headers_schema: Optional[Type[schemas.DictSchema]] = None @classmethod def deserialize(cls, response: urllib3.HTTPResponse, configuration: schema_configuration_.SchemaConfiguration) -> T: # 原有反序列化逻辑 deserialized_headers = ... deserialized_body = ... return cls.response_cls( response=response, headers=deserialized_headers, body=deserialized_body )
这样Mypy能明确cls.response_cls是类变量,且类型匹配泛型T,直接消除歧义错误。
2. 更优结构:用抽象类强制子类实现response_cls
如果需要确保所有子类都必须提供response_cls,可以结合abc.ABCMeta定义抽象类,将response_cls设为抽象类变量:
import abc import typing from typing import Type, Generic, Optional, Dict class OpenApiResponse(typing.Generic[T], metaclass=abc.ABCMeta): # 抽象类变量,强制子类赋值 response_cls: typing.Type[T] content: Optional[Dict[str, MediaType]] = None headers: Optional[Dict[str, Type[HeaderParameterWithoutName]]] = None headers_schema: Optional[Type[schemas.DictSchema]] = None @classmethod def deserialize(cls, response: urllib3.HTTPResponse, configuration: schema_configuration_.SchemaConfiguration) -> T: # 原有反序列化逻辑 deserialized_headers = ... deserialized_body = ... return cls.response_cls( response=response, headers=deserialized_headers, body=deserialized_body )
这种方式下,若子类未定义response_cls,Python会直接抛出错误,同时Mypy也能正确推断类型,避免运行时问题。
3. 可选方案:用抽象类方法get_response_cls
如果偏好方法式的实现,也可以定义抽象类方法强制子类返回response_cls:
import abc import typing from typing import Type, Generic, Optional, Dict class OpenApiResponse(typing.Generic[T], metaclass=abc.ABCMeta): content: Optional[Dict[str, MediaType]] = None headers: Optional[Dict[str, Type[HeaderParameterWithoutName]]] = None headers_schema: Optional[Type[schemas.DictSchema]] = None @classmethod @abc.abstractmethod def get_response_cls(cls) -> typing.Type[T]: raise NotImplementedError @classmethod def deserialize(cls, response: urllib3.HTTPResponse, configuration: schema_configuration_.SchemaConfiguration) -> T: # 原有反序列化逻辑 deserialized_headers = ... deserialized_body = ... response_cls = cls.get_response_cls() return response_cls( response=response, headers=deserialized_headers, body=deserialized_body )
子类实现示例:
class SuccessfulXmlAndJsonArrayOfPet(api_client.OpenApiResponse[ApiResponse]): @classmethod def get_response_cls(cls) -> typing.Type[ApiResponse]: return ApiResponse # 其他内容定义...
但考虑到你倾向于使用类变量,前两种方案更贴合需求。
总结
- 核心问题是
response_cls被错误定义为dataclass实例字段,在类方法中访问导致Mypy歧义,改为类变量即可解决。 - 若要保证子类规范,推荐抽象类+抽象类变量的方式,兼顾类型安全与代码约束。
内容的提问来源于stack exchange,提问作者spacether
相关产品推荐
相关产品推荐

