如何向抽象类方法构造函数添加类型注释?

How to add type annotation to abstract classmethod constructor?

我想类型注释抽象 class 方法 充当构造函数。例如在下面的代码中,ElementBase.from_data 是一个抽象的 class 方法构造函数。

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 returns 对象的所有子 class 在调用 [=16 时具有它的类型=].

[编辑] 注释掉 abstractmethod

看起来 mypy 不理解 abstractclassmethod 装饰器。自 Python 3.3 以来,该装饰器已被弃用,因为 abstractmethodclassmethod 装饰器已更新为可以很好地协同工作。如果您这样做,我认为您的代码将正常工作:

@classmethod
@abstractmethod 
def from_data(cls: Type[ElementT], data: int) -> ElementT:
    pass

它与您的类型检查问题无关,但您可能还想更改 ElementBase 以继承自 abc.ABC 或明确请求 abc.ABCMeta 元 class 如果您希望 class 的抽象性由 Python 强制执行。常规 classes 不关心 abstractmethod 装饰器,因此如所写,您将能够实例化 ElementBase (或者如果 __init__ 方法没有没有不相关的问题)。

还有一个关于这种类型提示的外围相关注释... PEP 673 将在 Python 3.11 中添加 typing.Self ,这将是一种方便的方法引用方式到它被调用的对象的类型。它应该可以很好地与 class 方法一起使用,而不需要您跳过任何障碍。有了它,您就可以编写这个更简单的注释版本:

@classmethod
@abstractmethod
def from_data(cls, data: int) -> Self:
    pass