键入以标记当前类类型或其任何子类的返回值



我想确保以下方法中的from_dict在其子类中也能很好地工作。目前,它的类型不能工作(mymyerror "不兼容的返回值类型")。我想是因为子类返回的是子类的一个实例,而不是超类的一个实例。

from __future__ import annotations
from abc import ABC
from dataclasses import dataclass
from typing import ClassVar, Type

@dataclass
class Statement(ABC):
@classmethod
def from_dict(cls) -> Statement:
return cls()

@dataclass
class Parent(ABC):
SIGNATURE_CLS: ClassVar[Type[Statement]]
def say(self) -> Statement:
# Initialize a Statement through a from_dict classmethod
return self.SIGNATURE_CLS.from_dict()

@dataclass
class ChildStatement(Statement):
pass

@dataclass
class Child(Parent, ABC):
SIGNATURE_CLS = ChildStatement
def say(self) -> ChildStatement:
# Initialize a ChildStatement through a from_dict classmethod
# that ChildStatement inherits from Statement
return self.SIGNATURE_CLS.from_dict()

上面的代码产生这个MyPy错误:

Incompatible return value type (got "Statement", expected "ChildStatement")  [return-value]

我认为这是StatementTypeVar的用例,但我不确定如何实现,特别是它背后的含义是什么。

子类实例is根据类型规则,它的超类的一个实例。您看到的错误是因为from_dict被键入为返回Statement,并且您试图从say返回该值,这保证返回ChildStatement。因此,您的问题是,您可能会返回更通用的Statement,而期望更具体的ChildStatement

你需要以某种方式确保MyPy:

  1. from_dict返回它的实际类,而不是通用的Statement
  2. ChildSIGNATURE_CLS将是ChildStatement,而不可能是Statement(仅仅分配ChildStatement是不够的,因为您显式地将其键入ClassVar[Type[Statement]]

你可以用下面的代码来完成:

  1. 使用打字。自我反映return cls()
from typing import Self
@dataclass
class Statement(ABC):
@classmethod
def from_dict(cls) -> Self:
return cls()

(重要的是要注意typing.Self是Python 3.11中引入的一个非常新的添加,您可能还需要更新您的MyPy以能够考虑到这一点)

  1. 显式键入Child.SIGNATURE_CLS
@dataclass
class Child(Parent, ABC):
SIGNATURE_CLS : ClassVar[Type[ChildStatement]] = ChildStatement

最后,我想知道Statement.from_dict()方法的敏感性,因为它与使用Statement()的标准构造函数没有什么不同。

最新更新