我想对充当构造函数的抽象类方法进行类型注释。例如,在下面的代码中,
ElementBase.from_data
意味着是一个抽象类方法构造函数。
tmp.py
from abc import abstractmethod, abstractclassmethod
import copy
from typing import TypeVar, Type
ElementT = TypeVar('ElementT', bound='ElementBase')
class ElementBase:
data: int
def __init__(self, data): self.data
#@abstractmethod
def get_plus_one(self: ElementT) -> ElementT:
out = copy.deepcopy(self)
out.data = self.data + 1
return out
@abstractclassmethod
def from_data(cls: Type[ElementT], data: int) -> ElementT: # mypy error!!!
pass
class Concrete(ElementBase):
@classmethod
def from_data(cls, data: int) -> 'Concrete': # mypy error!!!
return cls(data)
但是,将 mypy 应用于此代码会显示以下错误。
tmp.py:18: error: The erased type of self "Type[tmp.ElementBase]" is not a supertype of its class "tmp.ElementBase"
tmp.py:23: error: Return type "Concrete" of "from_data" incompatible with return type <nothing> in supertype "ElementBase"
您有办法解决这个错误吗?另外,我特别困惑的是
get_plus_one
的部分不会导致错误,而只有 abstractclassmethod
的部分会导致错误。
仅供参考,我想让抽象方法构造函数通用,因为我想静态地确保
ElementBase
的所有子类在调用 from_data
时返回其类型的对象。
[编辑]注释掉
abstractmethod
看起来
mypy
不理解abstractclassmethod
装饰器。该装饰器自 Python 3.3 起已被弃用,因为 abstractmethod
和 classmethod
装饰器已更新为可以很好地协同工作。我认为如果您这样做,您的代码将正常工作:
@classmethod
@abstractmethod
def from_data(cls: Type[ElementT], data: int) -> ElementT:
pass
这与您的类型检查问题无关,但您可能还希望将
ElementBase
更改为从 abc.ABC
继承,或者如果您希望 Python 强制执行类的抽象性,则显式请求 abc.ABCMeta
元类。常规类不关心 abstractmethod
装饰器,因此正如所写,您将能够实例化 ElementBase
(或者如果它的 __init__
方法没有不相关的问题,您也可以)。
关于这种类型提示的另一个外围相关注释... PEP 673 将在 Python 3.11 中添加
typing.Self
,这将是方法引用其所调用的对象类型的便捷方式。它应该可以很好地与类方法配合使用,而不需要您跳过任何障碍。有了它,您就可以编写这个更简单版本的注释:
@classmethod
@abstractmethod
def from_data(cls, data: int) -> Self:
pass