如何在继承自 dict 的类上键入注释运算符 `__ior__`?

问题描述 投票:0回答:1

我正在定义一个专门的

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

python python-typing mypy
1个回答
0
投票

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__

© www.soinside.com 2019 - 2024. All rights reserved.