如何正确为自定义MutableMapping实现添加类型注解?
问题:重写MutableMapping的update方法时mypy签名不兼容报错
我编写了如下继承自MutableMapping的子类:
from typing import Hashable, Any, MutableMapping from _typeshed import SupportsKeysAndGetItem class MyMutableMapping(MutableMapping[Hashable, Any]): def update(self, other: SupportsKeysAndGetItem[Hashable, Any], /, **kwargs: Any) -> None: pass
但mypy对重写的update方法签名提出报错:
test.py:5: error: Signature of "update" incompatible with supertype "MutableMapping" [override] test.py:5: note: Superclass: test.py:5: note: @overload test.py:5: note: def update(self, SupportsKeysAndGetItem[Hashable, Any], /, **kwargs: Any) -> None test.py:5: note: @overload test.py:5: note: def update(self, Iterable[tuple[Hashable, Any]], /, **kwargs: Any) -> None test.py:5: note: @overload test.py:5: note: def update(self, **kwargs: Any) -> None test.py:5: note: Subclass: test.py:5: note: def update(self, SupportsKeysAndGetItem[Hashable, Any], /, **kwargs: Any) -> None Found 1 error in 1 file (checked 1 source file)
我尝试复制mypy提示的父类签名,但仍然无法解决问题。请问我哪里出错了?
编辑:我已在mypy的GitHub问题追踪器提交了一个issue。
解答
问题核心是MutableMapping的update方法包含三个重载签名,你只实现了其中一种情况,mypy要求子类必须覆盖父类所有重载,或者提供一个兼容所有输入场景的单一签名。
解决方法1:实现所有重载版本
导入Iterable和overload,先声明所有三个重载的签名,再编写兼容所有情况的具体实现:
from typing import Hashable, Any, MutableMapping, Iterable, overload from _typeshed import SupportsKeysAndGetItem class MyMutableMapping(MutableMapping[Hashable, Any]): @overload def update(self, other: SupportsKeysAndGetItem[Hashable, Any], /, **kwargs: Any) -> None: ... @overload def update(self, other: Iterable[tuple[Hashable, Any]], /, **kwargs: Any) -> None: ... @overload def update(self, **kwargs: Any) -> None: ... def update(self, other: SupportsKeysAndGetItem[Hashable, Any] | Iterable[tuple[Hashable, Any]] | None = None, /, **kwargs: Any) -> None: # 处理传入的映射对象或键值对序列 if other is not None: if hasattr(other, "__getitem__"): for key in other: self[key] = other[key] else: for key, value in other: self[key] = value # 处理关键字参数 for key, value in kwargs.items(): self[key] = value
解决方法2:使用兼容的单一签名
如果不想声明多个重载,可以直接定义一个能接收所有合法输入的签名,让mypy判定其兼容父类的所有重载:
from typing import Hashable, Any, MutableMapping, Iterable, Union from _typeshed import SupportsKeysAndGetItem class MyMutableMapping(MutableMapping[Hashable, Any]): def update(self, other: Union[SupportsKeysAndGetItem[Hashable, Any], Iterable[tuple[Hashable, Any]]] | None = None, /, **kwargs: Any) -> None: # 实现逻辑同上 if other is not None: if hasattr(other, "__getitem__"): for key in other: self[key] = other[key] else: for key, value in other: self[key] = value for key, value in kwargs.items(): self[key] = value
为什么会报错?
父类MutableMapping的update支持三种合法调用方式:
- 传入一个映射类对象(如
dict,对应SupportsKeysAndGetItem类型) - 传入可迭代的键值对元组序列(如
[(k1, v1), (k2, v2)]) - 直接传入关键字参数(如
update(a=1, b=2))
你的子类只处理了第一种情况,因此mypy判定签名与父类不兼容。即使复制了父类的单个重载,也必须覆盖全部重载场景,或者用一个兼容的签名统合所有情况。
内容的提问来源于stack exchange,提问作者Nobilis
相关产品推荐
相关产品推荐

