实现MutableSequence时__setitem__调用重载函数报mypy类型不兼容错误
问题解决
错误核心原因
mypy不会在重载函数的实现逻辑中自动推导不同参数之间的绑定关系,你声明的__setitem__重载规则只对外部调用生效,在实现函数内部,mypy只会将参数识别为独立的联合类型:unsafe_index: SupportsIndex | slice、value: T | Iterable[T],不知道二者的类型是一一绑定的,因此抛出三个关联错误。
三个错误的具体诱因:
- 第34行错误:mypy无法确认实现函数的参数组合覆盖了所有重载声明的合法组合,担心出现
索引 + 可迭代对象、切片 + 单个值的非法调用。 - 第38行第一个错误:
_normalise_index返回值是slice | int的联合类型,mypy不知道当前场景应该匹配列表的索引还是切片语法。 - 第38行第二个错误:
value是T | Iterable[T]的联合类型,mypy不知道应该匹配列表单个位置赋值还是切片赋值的类型要求。
修复方案
在__setitem__实现内部对索引类型做显式窄化,配合cast标注对应分支的value类型即可解决所有错误,重载规则本身没有问题。
修复后的完整代码:
from typing import Iterable, SupportsIndex, TypeVar, overload, cast T = TypeVar("T") class Example: def __init__(self, iterable: Iterable[T]): self._lst = list(iterable) def __len__(self) -> int: return len(self._lst) @overload def _normalise_index(self, index: slice) -> slice: ... @overload def _normalise_index(self, index: SupportsIndex) -> int: ... def _normalise_index(self, index: slice | SupportsIndex) -> slice | int: "将负索引转换为正索引,实际业务场景下该逻辑合理" if isinstance(index, slice): return slice( 0 if not index.start else self._normalise_index(index.start), len(self) if not index.stop else self._normalise_index(index.stop), index.step or 1, ) else: return len(self) + idx if (idx := int(index)) < 0 else idx @overload def __setitem__(self, unsafe_index: SupportsIndex, value: T) -> None: ... @overload def __setitem__(self, unsafe_index: slice, value: Iterable[T]) -> None: ... def __setitem__( self, unsafe_index: SupportsIndex | slice, value: T | Iterable[T] ) -> None: if isinstance(unsafe_index, slice): # 窄化后mypy可推导出_normalise_index返回slice normalised_index = self._normalise_index(unsafe_index) # 重载已保证切片场景下value为Iterable[T],cast无运行时开销 self._lst[normalised_index] = cast(Iterable[T], value) else: # 窄化后mypy可推导出_normalise_index返回int normalised_index = self._normalise_index(unsafe_index) # 重载已保证索引场景下value为T self._lst[normalised_index] = cast(T, value)
说明
如果不想用cast,也可以在分支内添加运行时类型检查,但要注意str、bytes等本身也是可迭代类型,容易对T为字符串的场景造成误判,因此使用cast是更高效的方案。
内容的提问来源于stack exchange,提问作者Greedo
相关产品推荐
相关产品推荐

