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

如何为抽象方法属性添加类型提示,消除mypy检查报错?

抽象类属性相关的mypy报错解决

问题场景

有两组看似逻辑等效的代码,其中一组能通过mypy检查,另一组却触发报错:

无报错的代码示例

from abc import ABC, abstractmethod

class TableLoader(ABC):
    @property
    @abstractmethod
    def FieldToDataHeaderKey(self):
        # dict of model dicts of field names and header keys
        pass

    def set_headers(self, custom_headers=None):
        for mdl in self.FieldToDataHeaderKey.keys():
            pass

class StudyTableLoader(TableLoader):
    FieldToDataHeaderKey = {
        Study.__name__: {
            "code": CODE_KEY,
            "name": NAME_KEY,
            "description": DESC_KEY,
        },
    }

触发报错的代码示例

from abc import ABC, abstractmethod

class PeakAnnotationsLoader(ABC):
    @property
    @abstractmethod
    def add_columns_dict(self):
        pass

    @classmethod
    def add_df_columns(cls, df_dict: dict):
        for sheet, column_dict in cls.add_columns_dict.items():
            # ^^^ 此行触发mypy报错
            ...

class IsocorrLoader(PeakAnnotationsLoader):
    add_columns_dict = {}

mypy报错信息

DataRepo/loaders/peak_annotations_loader.py:213: error: "Callable[[PeakAnnotationsLoader], Any]" has no attribute "items"  [attr-defined]
DataRepo/loaders/peak_annotations_loader.py:321: error: Need type annotation for "add_columns_dict" (hint: "add_columns_dict: Dict[<type>, <type>] = ...")  [var-annotated]

报错原因

  1. 访问方式不匹配:第一个示例中set_headers是实例方法,通过self.FieldToDataHeaderKey访问属性——子类的FieldToDataHeaderKey是类属性,Python允许实例访问类属性,mypy能正确推断其为字典类型,因此调用.keys()无问题。而第二个示例中add_df_columns是类方法,通过cls.add_columns_dict访问时,抽象类中定义的add_columns_dict是实例属性的getter方法(@property修饰的抽象方法),mypy会将cls.add_columns_dict识别为可调用对象(而非字典),因此报错“无items属性”。
  2. 缺少类型注解:子类IsocorrLoader中的add_columns_dict未添加类型注解,触发mypy的var-annotated检查。

解决方法

方案1:将类方法改为实例方法(对齐第一个示例的逻辑)

如果业务逻辑允许,把add_df_columns改为实例方法,通过self访问属性,同时给抽象属性和子类属性添加明确的类型注解:

from abc import ABC, abstractmethod
from typing import Dict, Any

class PeakAnnotationsLoader(ABC):
    @property
    @abstractmethod
    def add_columns_dict(self) -> Dict[Any, Any]:
        pass

    # 改为实例方法
    def add_df_columns(self, df_dict: dict):
        for sheet, column_dict in self.add_columns_dict.items():
            ...

class IsocorrLoader(PeakAnnotationsLoader):
    # 添加类型注解,根据实际业务调整具体类型
    add_columns_dict: Dict[Any, Any] = {}

方案2:定义类级别的抽象属性(适配类方法场景)

如果必须保留类方法,需要将add_columns_dict定义为类级别的抽象属性,使用ClassVar标注类型,确保mypy识别其为类属性而非实例属性的getter:

from abc import ABC
from typing import Dict, Any, ClassVar

class PeakAnnotationsLoader(ABC):
    # 声明类级别的抽象属性类型
    add_columns_dict: ClassVar[Dict[Any, Any]]

    @classmethod
    def add_df_columns(cls, df_dict: dict):
        for sheet, column_dict in cls.add_columns_dict.items():
            ...

class IsocorrLoader(PeakAnnotationsLoader):
    # 实现类属性并添加类型注解
    add_columns_dict: ClassVar[Dict[Any, Any]] = {}

若需要强制子类必须实现该属性,可以在抽象类中添加__init_subclass__进行校验:

from abc import ABC
from typing import Dict, Any, ClassVar

class PeakAnnotationsLoader(ABC):
    add_columns_dict: ClassVar[Dict[Any, Any]]

    def __init_subclass__(cls) -> None:
        super().__init_subclass__()
        if not hasattr(cls, 'add_columns_dict'):
            raise NotImplementedError("所有子类必须定义add_columns_dict类属性")

    @classmethod
    def add_df_columns(cls, df_dict: dict):
        for sheet, column_dict in cls.add_columns_dict.items():
            ...

class IsocorrLoader(PeakAnnotationsLoader):
    add_columns_dict: ClassVar[Dict[Any, Any]] = {}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 01:37:14