mypy "Incompatible types"当一个@overload函数在__setitem__中调用另一个函数时出错



我正在创建一个实现MutableSequence契约的类,并且在提示getters&以sliceSupportsIndex实例作为索引参数的setter。在我的应用程序中,我有一个_normalise_index函数,它通过从len中减去(例如collection[:-1] -> collection[:5](来将负的相对索引和切片转换为正的,但mypy对我的过载不满意。。。

下面是一个简化的例子:

from typing import Iterable, SupportsIndex, TypeVar, overload
T = TypeVar("T")

class Example:  # in my real code this is `class Example(typing.MutableSequence[T])`, but that's not required for this minrepro
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:
"Convert -ve to +ve index - this makes sense for the real code..."
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__(  # line 34
self, unsafe_index: SupportsIndex | slice, value: T | Iterable[T]
) -> None:
normalised_index = self._normalise_index(unsafe_index)
self._lst[normalised_index] = value  # line 38

它给出了3个错误:

main.py:34: error: Overloaded function implementation does not accept all possible arguments of signature 2
main.py:38: error: Invalid index type "Union[int, slice]" for "List[T]"; expected type "SupportsIndex"
main.py:38: error: Incompatible types in assignment (expression has type "Union[T, Iterable[T]]", target has type "T")

在线mypy游乐场中查看


看起来mypy忽略了__setitem__的重载,所以它认为unsafe_index可能是sliceSupportsIndex,然后它只匹配_normalise_index的重载之一。不知道为什么。。。

我不会在python 3.8上收到您的第一个错误。对于后者,您将达到python的过载限制。你可以cast你的类型到什么过载状态:

def __setitem__(
self, unsafe_index: SupportsIndex | slice, value: T | Iterable[T]
) -> None:
normalised_index = self._normalise_index(unsafe_index)
if isinstance(normalised_index, slice):
self._lst[normalised_index] = cast(Iterable[T], value)
else:
self._lst[normalised_index] = cast(T, value)

这对我有效。如果使用@overload:,请不要在函数签名中重复注释

from typing import Iterable, SupportsIndex, TypeVar, Union, overload
T = TypeVar("T", bound=Union[int, str])

class Example:      
def __init__(self, iterable: Iterable[T]) -> None:
self._lst:list[T] = 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 | int:
"Convert -ve to +ve index - this makes sense for the real code..."
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__(# line 34
self, unsafe_index, value
) -> None:
normalised_index = self._normalise_index(unsafe_index)
self._lst[normalised_index] = value  # line 38

class SI(SupportsIndex):
def __index__(self, v):
pass

x = Example([])._normalise_index(slice(1, 2))
y = Example([])._normalise_index(SI())
Example([])[3] = 4
Example([])[3:4] = [4]

相关内容

  • 没有找到相关文章

最新更新