mypy:包含不同子类实例的字典

mypy: Dict containing instances of different subclasses

在下面的代码示例中,我们有一个包含抽象基础实例 class 及其子类型实例的字典。

from typing import Dict, Union


class Base:
    def __init__(self):
        self.x = 0


class Sub(Base):
    def __init__(self):
        super().__init__()
        self.y = 1


d: Dict[str, Base] = {
    'base': Base(),
    'sub': Sub()
}

print(d['sub'].y)

访问子类型的实例变量导致 Pycharm 中的 linter 警告 Unresolved attribute reference 'y' for class 'Base'

使用 mypy 检查此示例会引发错误:

error: Item "Base" of "Union[Base, Sub]" has no attribute "y"

将代码更改为

d: Dict[str, Union[Base, Sub]] = {
    'base': Base(),
    'sub': Sub()
}

修复了 Pycharm 中的 linter 警告,但在 mypy 中仍然出现错误。

mypy docs我知道"most mutable generic collections are invariant"。因此我假设 dict 是 "invariant"?

这是否意味着在 mypy 中不可能有一个包含不同子 classes 实例的字典?如果是这样,是否可以以某种方式更改此代码以使其通过 mypy?

由于密钥在运行前是未知的,我假设 TypedDict 不是一个选项?

从值访问一个字段,而它的类型可以是没有该字段的类型(Base 在您的情况下)——从 mypy 的角度(以及我的角度)来看,这是一个错误。

您应该重新考虑您的 类 和继承层次结构,或者添加 isinstance 检查

... # same as before
value = d['sub']
if isinstance(value, Sub):
    print(value.y)

mypy会说出类似

的内容
Success: no issues found in 1 source file

您可以使用 cast 告诉 mypy 是的,您承诺 d['sub'] 将是 Sub 的实例(不仅仅是 [= 的未指定子类) 15=]),它将具有 y 属性。

print(typing.cast(Sub, d['sub']).y)