mypy 抱怨扩展基础 class' 属性类型

mypy complains about extended base class' attribute type

我有两个基本 classes AB 定义如下:

class A(object):
    def common_function(self):
        pass
class B(object):
    def __init__(self, a: A):
        self.a = a
    def another_common_function(self):
        pass

ClassA保存了一些管理信息,而classB保存了一些其他信息,这些信息是基于classA,因此它知道它是 A.

的实例

我还有两个派生的 classes dAdB 是这样定义的:

class dA(A):
    def __init__(self, t: B):
        self.t = t
class dB(B):
    def __init__(self, a: dA):
        super(A, self).__init__(a)

这些 classes(除其他外 (dA1dB1dA2dB2) ... 设计相似)被使用对于某些特殊操作,因此他们需要存储更多信息,例如这对 classes 的示例中的 t,其他 classes 有不同的东西要存储。

问题是,mypy 抱怨 dB.a.t:

的用法
class dB(B):
    def __init__(self, a: dA):
        super(A, self).__init__(a)
    def do(self):
        if self.a.t is None:
            print("something")

test.py: error: "A" has no attribute "t"

抱怨其实是对的。 A 没有属性 t。我还告诉 mypy B.aA 类型,但在这种特殊情况下,我使用 dB.a 作为 dA 类型,它实际上有一个 t,但我明确告诉 mypy否则。

问题是:

  1. 这是否违反里氏原则?
  2. 如果不是 1,有没有办法告诉 mypy 在这种特殊情况下 dB.adA 类型?我需要使用 TypeVar 吗?
  3. 如果是 1,有没有办法重构 classes 以不违反 Liskov 原则以及让类型检查器能够识别正确的类型?

我发现了问题 ,但是,扩展基础 class 的解决方案不可行,因为这会使 t 在所有派生 class 中可用es,不仅dA(不知何故闻起来很糟糕)。

可以使用 assert:

来确保 self.adA 类型
class dB(B):
    def __init__(self, a: dA):
        super(A, self).__init__(a)
    def do(self):
        assert isinstance(self.a, dA)
        if self.a.t is None:
            print("something")

这个断言被 mypy 识别,因此 self.a 之后被称为 dA 的实例,因此具有属性 t.