重写这个问题,因为事实证明我认为的问题并不是问题。事实上,我有一个看似相同的情况,mypy 并没有抱怨。
这是一个没有看似等效的 mypy 投诉的示例:
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():
class StudyTableLoader(TableLoader):
FieldToDataHeaderKey = {
Study.__name__: {
"code": CODE_KEY,
"name": NAME_KEY,
"description": DESC_KEY,
},
}
但是,这个看似等效的代码会产生 mypy 错误:
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():
# ^^^ This is the line that produces the error
...
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]
我不知道如何在第二种情况下让我的朋友开心。顺便说一句,所有这些代码都有效。我如何在这里满足我的要求?
我应该有不同的方式来创建抽象类属性吗?我有多个继承自
TableLoader
的类,并且它们都不会产生 mypy 错误 WRT FieldToDataHeaderKey
。难道只是 mypy 没有像处理 .items()
那样处理 .keys()
?
在
PeakAnnotationsLoader
类中, add_columns_dict
方法位于实例级别,而 add_df_columns
方法位于类级别。使用 cls.add_columns_dict
就像使用 PeakAnnotationsLoaderChildClass.add_columns_dict
一样,理论上返回 mypy 所说的返回值,一个可调用的。
代码之所以有效,是因为您重写了
add_columns_dict
方法并将其设为静态字段,因此 PeakAnnotationsLoaderChildClass.add_columns_dict
返回该字段的值。父类不会以任何方式强制子类覆盖 add_columns_dict
,因此 mypy 会抛出此错误。
如果通过删除
add_columns_dict
注释使 @classmethod
方法成为实例级方法,该错误就会消失。
覆盖抽象属性的预期方法如下:
class IsocorrLoader(PeakAnnotationsLoader):
@property
def add_columns_dict(self) -> Optional[Dict[str, Dict[str, function]]]:
...
这会破坏你的代码,并且它不再起作用,因为你不再将
add_columns_dict
设为类的静态字段。
根据我目前的知识,不可能有一个抽象类方法同时也是一个属性。
使
add_columns_dict
成为抽象类方法而不是抽象属性也可以使此错误消失。您的代码可能如下所示:
class PeakAnnotationsLoader(ABC):
@classmethod
@abstractmethod
def get_add_columns_dict(self) -> Optional[Dict[str, Dict[str, function]]]:
pass
@classmethod
def add_df_columns(cls, df_dict: dict):
add_columns_dict = cls.get_add_columns_dict()
if add_columns_dict is not None:
for sheet, column_dict in add_columns_dict.items():
...
class IsocorrLoader(PeakAnnotationsLoader):
@classmethod
def get_add_columns_dict(self) -> Optional[Dict[str, Dict[str, function]]]:
...