如何在子类中向mypy声明属性不可为None?
问题描述
现有如下代码:
from dataclasses import dataclass @dataclass class MaybeTextContainer: text: list[str] | None class CertainlyText(MaybeTextContainer): def __init__(self) -> None: super().__init__(text=[]) self.say_hi() def say_hi(self) -> None: self.text.append("Hi")
运行mypy时,最后一行会报错:error: Item "None" of "list[str] | None" has no attribute "append"。
虽然可以在say_hi函数中添加assert self.text来解决,但如果多个函数都要修改self.text,这种方式过于繁琐。想知道有没有通用方法可以向mypy表明子类中的self.text属性不可为None?
解决方案
方法一:子类中重新注解属性类型
直接在子类里重新声明text属性,将类型明确为list[str],mypy会认可子类的类型覆盖:
from dataclasses import dataclass @dataclass class MaybeTextContainer: text: list[str] | None class CertainlyText(MaybeTextContainer): text: list[str] # 重新注解,指定属性不可为None def __init__(self) -> None: super().__init__(text=[]) self.say_hi() def say_hi(self) -> None: self.text.append("Hi") # mypy不再报错
注意:必须保证子类初始化逻辑确实给text赋值了非None值,避免运行时出现异常。
方法二:使用泛型构建灵活基类
如果有多个类似场景,可通过泛型定义基类,让子类指定具体的属性类型:
from dataclasses import dataclass from typing import TypeVar, Generic T = TypeVar("T") @dataclass class TextContainer(Generic[T]): text: T class MaybeTextContainer(TextContainer[list[str] | None]): pass class CertainlyText(TextContainer[list[str]]): def __init__(self) -> None: super().__init__(text=[]) self.say_hi() def say_hi(self) -> None: self.text.append("Hi")
这种方式能清晰区分不同子类的属性约束,扩展性更强。
方法三:初始化时统一类型转换
在子类的__init__方法中,用cast明确告知mypyself.text的实际类型,后续所有方法均可直接使用:
from dataclasses import dataclass from typing import cast @dataclass class MaybeTextContainer: text: list[str] | None class CertainlyText(MaybeTextContainer): def __init__(self) -> None: super().__init__(text=[]) self.text = cast(list[str], self.text) # 统一做类型注解转换 self.say_hi() def say_hi(self) -> None: self.text.append("Hi") # mypy不再报错
这种方式无需重新声明属性,仅需在初始化阶段做一次类型断言即可。
内容的提问来源于stack exchange,提问作者Frank Meulenaar
相关产品推荐
相关产品推荐

