如何为子类实例列表做类型注解并合并列表通过mypy类型检查?
解决方法
这里有几个不用# type: ignore就能让mypy通过的方案,核心是解决列表类型不变性带来的类型不兼容问题:
1. 显式标注子类列表的类型为父类列表
既然Cat是Animal的子类,你可以直接把cats的类型声明为list[Animal],这样它和animals的类型就完全匹配,+运算自然能通过mypy检查:
cats: list[Animal] = [Cat(height=1, weight=2, lives=7), Cat(height=3, weight=2, lives=1)] animals = [Animal(height=9, weight=9)] combined: Iterable[Animal] = cats + animals
mypy会认可这种标注,因为Cat实例完全符合Animal的类型要求。
2. 使用itertools.chain合并迭代器
如果你不想修改原列表的类型标注,可以用itertools.chain来合并两个可迭代对象。因为Iterable[Cat]是Iterable[Animal]的协变子类型,mypy会允许这种合并:
from itertools import chain cats = [Cat(height=1, weight=2, lives=7), Cat(height=3, weight=2, lives=1)] animals = [Animal(height=9, weight=9)] combined: Iterable[Animal] = chain(cats, animals)
这种方式不会创建新的列表,而是返回一个迭代器,遍历效果和列表一致,还能节省内存。
3. 显式转换子类列表的类型
如果你需要保留cats原有的list[Cat]类型,又想合并后得到list[Animal],可以显式转换类型:
cats = [Cat(height=1, weight=2, lives=7), Cat(height=3, weight=2, lives=1)] animals = [Animal(height=9, weight=9)] combined: list[Animal] = list[Animal](cats) + animals
不过这种方式会创建新的列表对象,相比第一种方法稍显繁琐。
为什么原代码会报错?
Mypy对列表类型采用**不变性(invariant)**检查——虽然Cat是Animal的子类,但list[Cat]并不被认为是list[Animal]的子类型。列表的类型参数不会随子类关系协变,所以直接用+合并list[Cat]和list[Animal]时,mypy会判定这两种类型不兼容。
内容的提问来源于stack exchange,提问作者Martin Thoma
相关产品推荐
相关产品推荐

