mypy中如何处理继承?

问题描述 投票:0回答:1

我正在尝试将 mypy 添加到我的 python 项目中,但我发现了一个障碍。假设我有以下继承:

class BaseClass:
    base_attribute: str


class A(BaseClass):
    attribute_for_class_A: str


class B(BaseClass):
    attribute_for_class_B: str

现在让我们创建一些处理这些类的两个实例的代码,但我们并不真正了解它:

@dataclass
class ClassUsingTheOthers:
    fields: Dict[str, BaseClass]

    def get_field(self, field_name: str) -> BaseClass:
        field = self.fields.get(field_name)
        if not field:
            raise ValueError('Not found')
        return field

这里最重要的是

get_field
方法。现在让我们创建一个函数来使用
get_field
方法,但该函数需要使用
BaseClass
的特定子类,
B
,例如:

def function_that_needs_an_instance_of_b(instance: B):
    print(instance.attribute_for_class_B)

现在如果我们一起使用所有代码,我们可以得到以下结果:

if __name__ == "__main__":
    class_using_the_others = ClassUsingTheOthers(
        fields={
            'name_1': A(),
            'name_2': B()
        }
    )
    function_that_needs_an_instance_of_b(class_using_the_others.get_field('name_2'))

显然,当我运行

mypy
到这个 file (在这个要点中你可以找到所有代码)时,我收到以下错误,正如预期的那样:

 error: Argument 1 to "function_that_needs_an_instance_of_b" has incompatible type "BaseClass"; expected "B"  [arg-type]

所以我的问题是,如何修复我的代码以使此错误消失?我无法更改

fields
属性的类型提示,因为我确实需要这样设置。有什么想法吗?我错过了什么吗?我应该检查返回字段的类型吗?

python inheritance mypy
1个回答
1
投票

我无法更改

fields
属性的类型提示

好吧,这就是你的答案。如果您将

fields
声明为值类型为
BaseClass
的字典,您如何期望任何静态类型检查器了解更多信息?

(相关:可变字典的类型注释

类型检查器不会根据您提供的任何键来区分字典的不同值。

如果您提前知道确切的键值对是什么,您可以使用

TypedDict
(如
@dROOOze
建议)来完成此操作,或者您可以编写一些丑陋的
overload
对于 Literal
 方法的 
field_name
使用不同的
get_field
 字符串值。

但由于您的限制,这些都不适用。

因此,您要么使用运行时断言来缩小类型(如 @juanpa.arrivillaga 所提到的),这是我推荐的,要么放置特定的

type: ignore[arg-type]
注释(如 @luk2302 所提到的)并完成它.

前者看起来像这样:

from dataclasses import dataclass

class BaseClass:
    base_attribute: str

@dataclass
class A(BaseClass):
    attribute_for_class_A: str

@dataclass
class B(BaseClass):
    attribute_for_class_B: str

@dataclass
class ClassUsingTheOthers:
    fields: dict[str, BaseClass]

    def get_field(self, field_name: str) -> BaseClass:
        field = self.fields.get(field_name)
        if not field:
            raise ValueError('Not found')
        return field

def function_that_needs_an_instance_of_b(instance: B) -> None:
    print(instance.attribute_for_class_B)

if __name__ == '__main__':
    class_using_the_others = ClassUsingTheOthers(
        fields={
            'name_1': A(attribute_for_class_A='foo'),
            'name_2': B(attribute_for_class_B='bar'),
        }
    )
    obj = class_using_the_others.get_field('name_2')
    assert isinstance(obj, B)
    function_that_needs_an_instance_of_b(obj)

这既能让

mypy
快乐,又能让你保持理智,如果你忘记了,你在那里期待什么价值。

© www.soinside.com 2019 - 2024. All rights reserved.