You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让mypy兼容__getitem__返回None、__setitem__拒接None的MutableMapping

解决方案

修复__contains__的类型错误

MutableMapping继承的Container和Mapping抽象类要求__contains__的参数类型为object(遵循里氏替换原则,子类方法的参数类型不能比父类更严格)。你需要修改参数类型,并在内部过滤非字符串的键:

def __contains__(self, key: object) -> bool:
    if not isinstance(key, str):
        return False
    return self._record.get(key) is not None

这样既符合父类的类型要求,又保证只有字符串键会被检查,非字符串键直接返回False,避免了和__getitem__的参数冲突。

修复__getitem__的类型错误

Mapping[str, int]的契约要求__getitem__必须返回int,当键不存在时抛出KeyError,而你的实现返回None违反了这个契约,这是mypy报错的核心原因。

如果必须保留“无对应键时返回None”的逻辑,同时不想移除MutableMapping继承,有两种可行方案:

方案1:遵循Mapping契约,新增get方法实现需求

调整__getitem__使其符合契约(键不存在时抛KeyError),同时新增一个get方法来实现“返回None”的逻辑:

def __getitem__(self, key: str) -> int:
    # 遵循Mapping契约,键不存在时抛出KeyError
    try:
        return self._record[key]
    except KeyError:
        # 替换为你的实际复杂逻辑后,抛出KeyError
        raise KeyError(key)

def get(self, key: str) -> int | None:
    # 实现你需要的“无键返回None”逻辑
    return self._record.get(key)

这种方案完全符合MutableMapping的类型规范,mypy会通过检查,同时满足你的业务需求。

方案2:调整泛型类型并限制__setitem__的参数

如果你坚持让__getitem__返回int | None,可以将泛型改为MutableMapping[str, int | None],然后通过重载和运行时检查限制__setitem__只能接受int类型:

from typing import overload, Iterator
from collections.abc import MutableMapping

class FooMap(MutableMapping[str, int | None]):
  
    _record: dict[str, int]
  
    def __init__(self) -> None:
        self._record = {}
  
    def __contains__(self, key: object) -> bool:
        if not isinstance(key, str):
            return False
        return self._record.get(key) is not None
  
    def __getitem__(self, key: str) -> int | None:
        return self._record.get(key)
  
    @overload
    def __setitem__(self, key: str, value: int) -> None: ...

    def __setitem__(self, key: str, value: int | None) -> None:
        if value is None:
            raise ValueError("Cannot set None as value")
        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会认为__setitem__支持int | None,但实际运行时会拒绝None值,同时__getitem__的返回类型也符合泛型定义。不过这种方式存在一定的类型“欺骗”,不如方案1规范。


内容的提问来源于stack exchange,提问作者InSync

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:52:02