我正在尝试找出一些抽象类的类型提示,我想把这些抽象类用作具有create
函数的类的基类,具体来说,这是为了反序列化类型。
我的简单示例如下所示
from abc import ABC, abstractmethod
from typing import Type, TypeVar
T = TypeVar("T", bound="A")
class A(ABC):
@classmethod
@abstractmethod
def create(cls: Type[T]) -> T:
pass
class B(A, ABC):
@classmethod
@abstractmethod
def create_b(cls: Type[T]) -> T:
pass
@classmethod
def create(cls) -> T:
return cls.create_b()
当我用Mypy和这个做对比时
错误:不兼容的返回值类型(获得“B”,应为“T”)
我对此感到困惑,因为B
继承自A
,而我认为T
或多或少代表“任何A
“。
我可以把倒数第二行改成
def create(cls: Type[T]) -> T:
但之后我就
错误:“类型[T]”没有属性“create_b”
我该怎么做才能让我的孩子通过呢?
1条答案
按热度按时间jv4diomz1#
由于
create
是一个类方法,参数cls
的类型为Type[B]
。这意味着在create_b
中为参数和返回类型指定的T
将被解析为B
,因此表达式cls.create_b()
的类型为B
。这将导致您得到的错误。这里令人困惑的部分可能是,由于
B
是A
的子类型,并且T
绑定到A
,因此人们可能期望应该可以在B.create()
中返回B
。T
将在B.create()
用于某些上下文时解析,而不是更早。在您的示例中,您已经隐式地将T
强制为B
,这可能会导致类型错误。例如,假设我们创建了以下类和函数。
现在我们可以使用
B.create()
作为foo
的参数:类型检查器在这里不会抱怨,因为它将
T
(create
的泛型返回类型)解析为C
,这非常好,因为T
由A
绑定,并且C
是A
的子类型。回顾
B.create()
的函数定义,我们现在可以看到,我们实际上必须返回抽象类型T
,而不是T
的某些允许的实现,例如B
(甚至A
)。TL; DR
您可以通过更改
B.create()
的返回类型来修复错误: