如何在Python自定义类中实现MutableSequence并使用@overload装饰器
实现说明
首先明确:Python 标准库的 @overload 装饰器仅用于类型标注,不会提供运行时的方法重载能力,它的作用是给类型检查工具(比如PyCharm、mypy)声明方法在不同入参下对应的返回值类型,运行时实际只会调用最后你实现的那个不带@overload的同名方法。
你只需要按照以下逻辑实现即可:
- 保留PyCharm生成的所有
@overload签名,删掉签名上的@abstractmethod装饰器(否则类会被识别为抽象类无法实例化) - 最后实现的通用方法里,自行判断参数类型(整数/切片)执行对应逻辑即可
完整可运行示例
from collections.abc import MutableSequence, Iterable from typing import TypeVar, overload T = TypeVar('T') class Test(MutableSequence[T]): # 构造函数,这里用内部列表存储实际数据,你可以替换为自己的存储逻辑 def __init__(self, data: Iterable[T] = ()): self._data = list(data) def insert(self, index: int, object: T) -> None: self._data.insert(index, object) # __getitem__的两个重载签名,仅做类型标注用 @overload def __getitem__(self, i: int) -> T: ... @overload def __getitem__(self, s: slice) -> MutableSequence[T]: ... # 实际运行时调用的实现 def __getitem__(self, item: int | slice) -> T | MutableSequence[T]: if isinstance(item, int): return self._data[item] elif isinstance(item, slice): # 切片返回同类型实例,符合MutableSequence的行为预期 return Test(self._data[item]) raise TypeError(f"索引类型只能是int或slice,当前为{type(item)}") # __setitem__的两个重载签名 @overload def __setitem__(self, i: int, o: T) -> None: ... @overload def __setitem__(self, s: slice, o: Iterable[T]) -> None: ... def __setitem__(self, key: int | slice, value: T | Iterable[T]) -> None: if isinstance(key, int): self._data[key] = value # type: ignore elif isinstance(key, slice): if not isinstance(value, Iterable): raise TypeError("切片赋值需要传入可迭代对象") self._data[key] = value else: raise TypeError(f"索引类型只能是int或slice,当前为{type(key)}") # __delitem__的两个重载签名 @overload def __delitem__(self, i: int) -> None: ... @overload def __delitem__(self, i: slice) -> None: ... def __delitem__(self, key: int | slice) -> None: self._data.__delitem__(key) def __len__(self) -> int: return len(self._data) # 可选:实现__repr__方便调试 def __repr__(self): return f"Test({self._data})" # 使用测试 if __name__ == "__main__": t = Test([1,2,3,4,5]) print(t[0]) # 输出1,整数索引取单个值 print(t[1:4]) # 输出Test([2,3,4]),切片返回同类型实例 t[0] = 10 print(t) # 输出Test([10,2,3,4,5]) t[1:3] = [20,30] print(t) # 输出Test([10,20,30,4,5]) del t[0] print(t) # 输出Test([20,30,4,5]) del t[1:] print(t) # 输出Test([20])
注意事项
- 所有带
@overload的方法不需要写实现,仅保留签名即可 - 实际实现的方法需要覆盖所有重载的参数场景,内部通过
isinstance判断参数类型走不同分支 - PyCharm会基于你写的
@overload签名做代码提示,比如整数索引取值会提示返回单个元素,切片取值会提示返回序列,赋值时也会做对应的类型校验
内容的提问来源于stack exchange,提问作者usan
相关产品推荐
相关产品推荐

