Mypy报错:重载函数签名重叠且返回类型不兼容,求解决方案
我希望自定义类的__getitem__重载方法,根据传入参数为str或Sequence[str]时,分别返回单个float或float序列。使用Python内置typing模块的@overload装饰器定义了两个重载方法及一个实现方法后,mypy在第一个重载方法行报错:"Overloaded function signatures 1 and 2 overlap with incompatible return types"。推测原因是str被识别为Sequence[str]导致歧义。
代码如下:
class SimulationResults: ... # Mypy error occurs here @overload def __getitem__(self: Self, names: str) -> float: ... @overload def __getitem__(self: Self, names: Sequence[str]) -> Sequence[float]: ... def __getitem__(self: Self, names: str | Sequence[str]) -> float | Sequence[float]: def get_value(name: str) -> float: if name == self._time_var_name: return self._time[self._current_index] if name not in self._vars: raise KeyError(f"Variable '{name}' does not exist for this simulation") return self._vars[name][self._current_index] return ( get_value(names) if isinstance(names, str) else tuple(get_value(name) for name in names) )
没错,问题出在str本身就是Sequence[str]的子类(Python中字符串属于字符序列)。mypy检测到当传入str类型参数时,两个重载签名都能匹配,但返回类型一个是float、一个是Sequence[float],类型不兼容,因此抛出重叠报错。
方案1:使用排除字符串的序列类型(Python 3.10+)
利用typing.NotStrSequence(专门排除str的序列类型)来明确第二个重载的参数范围,消除歧义:
from typing import Self, Sequence, overload, NotStrSequence class SimulationResults: ... @overload def __getitem__(self: Self, names: str) -> float: ... @overload def __getitem__(self: Self, names: NotStrSequence[str]) -> Sequence[float]: ... def __getitem__(self: Self, names: str | NotStrSequence[str]) -> float | Sequence[float]: # 原实现代码不变 def get_value(name: str) -> float: if name == self._time_var_name: return self._time[self._current_index] if name not in self._vars: raise KeyError(f"Variable '{name}' does not exist for this simulation") return self._vars[name][self._current_index] return ( get_value(names) if isinstance(names, str) else tuple(get_value(name) for name in names) )
如果使用低于3.10的Python版本,可以自行定义排除str的序列类型:
from typing import Self, Sequence, overload, TypeVar from collections.abc import Sequence as ABCSequence # 定义仅匹配非str的字符串序列类型 NonStrSequenceStr = TypeVar('NonStrSequenceStr', bound=ABCSequence[str]) class SimulationResults: ... @overload def __getitem__(self: Self, names: str) -> float: ... @overload def __getitem__(self: Self, names: NonStrSequenceStr) -> Sequence[float]: ... def __getitem__(self: Self, names: str | NonStrSequenceStr) -> float | Sequence[float]: # 原实现代码不变 ...
方案2:拆分方法,规避重载歧义
放弃在__getitem__中做重载,改为提供两个独立方法分别处理单字符串和序列场景,同时保留__getitem__作为统一入口:
from typing import Self, Sequence class SimulationResults: ... def _get_single_value(self, name: str) -> float: if name == self._time_var_name: return self._time[self._current_index] if name not in self._vars: raise KeyError(f"Variable '{name}' does not exist for this simulation") return self._vars[name][self._current_index] def get(self, name: str) -> float: return self._get_single_value(name) def get_many(self, names: Sequence[str]) -> Sequence[float]: return tuple(self._get_single_value(name) for name in names) def __getitem__(self, names: str | Sequence[str]) -> float | Sequence[float]: if isinstance(names, str): return self.get(names) return self.get_many(names)
这种方式逻辑更清晰,也彻底避免了类型重载的歧义问题。
方案3:使用类型守卫辅助mypy识别(结合参数类型调整)
自定义类型守卫函数,帮助mypy更精准判断参数类型,同时配合调整重载的参数范围:
from typing import Self, Sequence, overload, TypeGuard from typing import NotStrSequence def is_single_name(names: str | NotStrSequence[str]) -> TypeGuard[str]: return isinstance(names, str) class SimulationResults: ... @overload def __getitem__(self: Self, names: str) -> float: ... @overload def __getitem__(self: Self, names: NotStrSequence[str]) -> Sequence[float]: ... def __getitem__(self: Self, names: str | NotStrSequence[str]) -> float | Sequence[float]: def get_value(name: str) -> float: if name == self._time_var_name: return self._time[self._current_index] if name not in self._vars: raise KeyError(f"Variable '{name}' does not exist for this simulation") return self._vars[name][self._current_index] if is_single_name(names): return get_value(names) return tuple(get_value(name) for name in names)
类型守卫能让mypy在类型检查时更准确地关联参数和返回类型,配合NotStrSequence可以彻底消除歧义。
内容的提问来源于stack exchange,提问作者Owen Page

