我正在尝试将 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
属性的类型提示,因为我确实需要这样设置。有什么想法吗?我错过了什么吗?我应该检查返回字段的类型吗?
我无法更改
属性的类型提示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
快乐,又能让你保持理智,如果你忘记了,你在那里期待什么价值。