如何让mypy满意我的MutableMapping[str, int],其__getitem__可以返回None并且__setitem__不能接受None?

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

我有以下类,它将

str
映射到其相应的
int
None
(如果没有这样的键)。我希望它成为
collections.abc.MutableMapping
的子类。真正的逻辑比
self._record.get()
要复杂一点,但整个事情可以归结为:

from typing import Iterator
from collections.abc import MutableMapping


class FooMap(MutableMapping[str, int]):
    
  _record: dict[str, int]
    
  def __init__(self) -> None:
    self._record = {}
    
  def __contains__(self, key: str) -> bool:
    return self[key] is not None
    
  def __getitem__(self, key: str) -> int | None:
    return self._record.get(key)
    
  def __setitem__(self, key: str, value: int) -> None:
    self._record[key] = value
    
  def __delitem__(self, key: str) -> None:
    raise TypeError('Keys cannot be deleted')
    
  def __len__(self) -> int:
    return len(self._record)
    
  def __iter__(self) -> Iterator[str]:
    return iter(self._record)

然而,

mypy
抱怨这两个
__contains__()

main.py:12: error: Argument 1 of "__contains__" is incompatible with supertype "Mapping"; supertype defines the argument type as "object"  [override]
main.py:12: note: This violates the Liskov substitution principle
main.py:12: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
main.py:12: error: Argument 1 of "__contains__" is incompatible with supertype "Container"; supertype defines the argument type as "object"  [override]

...以及

__getitem__()
方法:

main.py:15: error: Return type "int | None" of "__getitem__" incompatible with return type "int" in supertype "Mapping"  [override]

我得到后者:

__getitem__()
Mapping[str, int]
方法应该返回
int
,而不是
None
,但这不是我的用例。将
int
更改为
int | None
没有帮助,因为
__setitem__()
的第二个参数也需要相应更改。

前者更令人困惑:传递给

__contains__()
的第一个参数应该是
str
,因为我们正在谈论
Mapping[str, int]
。然而,根据
它给我的链接
,mypy 期望比object更通用的东西。我把它改成了
key: object
但是没有效果,因为
__getitem__()
想要一个
str

我知道我可以扔掉

MutableMapping
或添加注释来明确告诉 mypy 它不需要仔细检查一行,但我也不想这样做。

如何在保留我最初的用例的同时让 mypy 满意?我可以使用 mypy 1.4+ 和 Python 3.11+ 支持的任何功能。

python type-hinting mypy abc
1个回答
0
投票

实际上可以

.register()
一个类作为
ABC
的“虚拟子类”:

from collections.abc import MutableMapping

class FooMap:
  ...

MutableMapping.register(FooMap)

这还支持运行时类型检查:

print(isinstance(FooMap(), MutableMapping))  # True

即使我最终没有使用这个(现在改变已经太晚了),它通过了

mypy
并且非常适合我的目的。

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