我正在定义一个专门的
dict
类。 (专业细节并不重要。)
我想对这个类进行注释(类型提示),特别是
__ior__
方法,但我还没有想出 MyPy 接受的任何注释。
我从这个开始:
from typing import Any
class MyDict(dict):
def __ior__(self, other: Any) -> MyDict:
self.update(other)
return self
MyPy 抱怨:
main.py:4: error: Signatures of "__ior__" and "__or__" are incompatible [misc]
因此,我在
__ior__
中定义 __or__
和 MyDict
与在 dict
中定义完全一样,根据 reveal_type(dict.__or__)
和 reveal_type(dict.__ior__)
:
from typing import overload, Any, Iterable, TypeVar, Union
from _typeshed import SupportsKeysAndGetItem
_KT = TypeVar("_KT")
_VT = TypeVar("_VT")
_T1 = TypeVar("_T1")
_T2 = TypeVar("_T2")
class MyDict(dict[_KT, _VT]):
def __init__(self, *args: Any, **kwargs: Any):
...
@overload
def __ior__(self: MyDict[_KT, _VT], other: "SupportsKeysAndGetItem[_KT, _VT]", /) -> MyDict[_KT, _VT]:
...
@overload
def __ior__(self: MyDict[_KT, _VT], other: Iterable[tuple[_KT, _VT]], /) -> MyDict[_KT, _VT]:
...
@overload
def __ior__(self: MyDict[_KT, _VT], other: "SupportsKeysAndGetItem[_T1, _T2]", /) -> MyDict[Union[_KT, _T1], Union[_VT, _T2]]:
...
def __ior__(self, other, /):
self.update(other)
return self
@overload
def __or__(self: MyDict[_KT, _VT], other: dict[_KT, _VT], /) -> MyDict[_KT, _VT]:
...
@overload
def __or__(self: MyDict[_KT, _VT], other: dict[_T1, _T2], /) -> MyDict[Union[_KT, _T1], Union[_VT, _T2]]:
...
def __or__(self, other, /):
new = MyDict(self)
new.update(other)
return new
这并不能解决
__ior__
/__or__
的不兼容性。 此外,它还增加了 MyDict.__ior__
和 dict.__ior__
之间的不匹配:
main.py:14: error: Signature of "__ior__" incompatible with "__or__" of supertype "dict" [override]
...
如果我添加一个符合
MyDict.__ior__
重载的dict.__ior__
重载,它需要一个dict[_T1,_T2]
,它似乎修复了MyDict.__ior__
和dict.__or__
之间的不匹配,但它并没有修复 __ior__
/__or__
不兼容。
为什么MyDict的
__ior__
和__or__
不兼容?
我在哪里可以找到 __ior__
和 __or__
兼容性的规则?
这是带有我的代码的 mypy Playground 的链接: https://gist.github.com/mypy-play/97023b90be45766db2844730e59d218c
TL;DR 忽略该诊断。
让我们看看 typeshed 是如何描述
dict.__or__
和 dict.__ior__
(这里):
class dict(MutableMapping[_KT, _VT]):
...
if sys.version_info >= (3, 9):
@overload
def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
@overload
def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
@overload
def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
@overload
def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
# dict.__ior__ should be kept roughly in line with MutableMapping.update()
@overload # type: ignore[misc]
def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ...
@overload
def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ...
注意第一次过载的
# type: ignore[misc]
。
诊断警告您
x | y
和 x |= y
(其中 x 是 dict
)接受不同类型的 y
。这通常是一个坏兆头,但是在 dict
的情况下,这就是我们的现实:
x: dict[int, int] = {}
x |= [(1, 2)] # fine, now x is ``{1: 2}``
x | [(1, 2)] # TypeError: unsupported operand type(s) for |: 'dict' and 'list'
所以你需要告诉类型检查器这不是一个错误,你确实支持不同的类型。
如果你想实现一个真正的类型安全
dict
子类,你应该使用与 typeshed 或更广泛的相同的重载(以避免 LSP 违规),并且对于 mypy
与 __or__
不兼容性保持沉默 __ior__
。