Mypy静态类型检查中虚拟子类部分无报错的原因咨询
你的困惑核心在于:mypy对Sized、Container、Iterable的类型检查依赖结构子类型(鸭子类型),而非虚拟子类的注册;但Sequence是名义类型,必须显式关联类型关系才会被认可。
具体拆解如下:
1. mypy对部分ABC采用协议(Protocol)处理
Sized、Container、Iterable在mypy的类型系统中被视为协议类型——只要你的类实现了该ABC要求的所有特殊方法,mypy就会认为它符合该类型,完全不需要通过abc.register()注册虚拟子类。
比如:
Sized仅要求实现__len__方法,你的SequenceLike满足,因此mypy认可它是Sized的子类型Container要求实现__contains__,你也已实现Iterable要求实现__iter__,同样满足
就算删掉代码里的abc.Sequence.register(SequenceLike)这一行,checkSized、checkContainer、checkIterable这三个调用依然不会触发mypy错误——这说明注册虚拟子类的操作根本没影响到mypy的判断。
2. Sequence是名义类型,不支持结构子类型匹配
abc.Sequence在mypy中是名义类型,它要求类型必须显式继承Sequence,或者通过typing.cast、显式类型注解声明关联关系,单纯实现方法或者注册虚拟子类都不被mypy认可。
这就是checkSequence(SequenceLike([1,2,3]))报错的原因:mypy只认Sequence的显式子类,不管你有没有注册虚拟子类,也不管你实现了多少Sequence要求的方法。
验证代码
去掉注册语句后的代码依然能通过前三个函数的类型检查:
from collections import abc class SequenceLike: def __init__(self, lst): self._list = list(lst) def __getitem__(self, index): return self._list[index] def __len__(self): return len(self._list) def __contains__(self, object): return object in self._list def __iter__(self): return iter(self._list) def checkSized(a: abc.Sized): pass def checkSequence(a: abc.Sequence[int]): pass def checkContainer(a: abc.Container): pass def checkIterable(a: abc.Iterable): pass # 移除虚拟子类注册语句 # abc.Sequence.register(SequenceLike) checkSized(SequenceLike([])) # 无类型错误 checkContainer(SequenceLike([1, 2])) # 无类型错误 checkIterable(SequenceLike([])) # 无类型错误 checkSequence(SequenceLike([1, 2, 3])) # 依然报错:类型不兼容
补充说明
《流畅的Python》里关于“静态类型检查器无法处理虚拟子类”的说法是准确的——你注册虚拟子类的操作确实没被mypy识别,但前三个函数没报错的原因是mypy用了结构子类型匹配,和虚拟子类无关。
内容的提问来源于stack exchange,提问作者Elin Anyu

