python 如何对调用另一个类方法来构造类示例的泛型类方法进行类型提示?

v64noz0r  于 2023-01-24  发布在  Python
关注(0)|答案(1)|浏览(110)

我正在尝试找出一些抽象类的类型提示,我想把这些抽象类用作具有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”
我该怎么做才能让我的孩子通过呢?

jv4diomz

jv4diomz1#

由于create是一个类方法,参数cls的类型为Type[B]。这意味着在create_b中为参数和返回类型指定的T将被解析为B,因此表达式cls.create_b()的类型为B。这将导致您得到的错误。
这里令人困惑的部分可能是,由于BA的子类型,并且T绑定到A,因此人们可能期望应该可以在B.create()中返回BT将在B.create()用于某些上下文时解析,而不是更早。在您的示例中,您已经隐式地将T强制为B,这可能会导致类型错误。
例如,假设我们创建了以下类和函数。

class C(A):
    pass

def foo(x: C):
    pass

现在我们可以使用B.create()作为foo的参数:

foo(B.create())

类型检查器在这里不会抱怨,因为它将Tcreate的泛型返回类型)解析为C,这非常好,因为TA绑定,并且CA的子类型。
回顾B.create()的函数定义,我们现在可以看到,我们实际上必须返回抽象类型T,而不是T的某些允许的实现,例如B(甚至A)。

TL; DR

您可以通过更改B.create()的返回类型来修复错误:

@classmethod
def create(cls) -> "B":
    return cls.create_b()

相关问题