如何为继承自dict的类的__ior__运算符添加类型注解?
问题分析与解决方案
核心原因
MyPy对运算符重载的签名有严格的兼容性要求,尤其是配对运算符(如__or__/__ior__)以及继承内置类型时:
- 配对运算符一致性:
__or__(|)和__ior__(|=)的参数类型必须兼容,返回类型也需要符合逻辑一致性——__or__返回新实例,__ior__返回原地修改后的self。 - Liskov替换原则:子类的方法签名不能比父类更严格。因为
MyDict继承自dict,你的__ior__必须接受dict.__ior__支持的所有参数类型(即Mapping[Any, Any]),同时返回类型可以是子类(协变返回,MyPy允许)。 - 最初的错误是因为
__ior__只接受Any而__or__的签名不匹配,后续重载没覆盖父类的参数范围,导致和dict.__ior__的签名冲突。
正确的类型注解实现
以下是让MyPy通过的示例代码:
from typing import Mapping, overload, Any, TypeVar K = TypeVar('K') V = TypeVar('V') class MyDict(dict[K, V]): @overload def __or__(self, other: Mapping[K, V]) -> 'MyDict[K, V]': ... @overload def __or__(self, other: Mapping[Any, Any]) -> 'MyDict[Any, Any]': ... def __or__(self, other: Mapping[Any, Any]) -> 'MyDict[Any, Any]': # 替换为你的实际实现逻辑 result = MyDict(self) result.update(other) return result @overload def __ior__(self, other: Mapping[K, V]) -> 'MyDict[K, V]': ... @overload def __ior__(self, other: Mapping[Any, Any]) -> 'MyDict[Any, Any]': ... def __ior__(self, other: Mapping[Any, Any]) -> 'MyDict[Any, Any]': super().__ior__(other) return self
关键说明
- 用
@overload同时提供精确泛型和兼容父类的签名,既满足类型精确性,又不违反父类的方法契约。 __or__和__ior__的参数类型范围一致,确保配对运算符的兼容性。__ior__返回MyDict而不是dict,这符合协变返回类型规则,MyPy允许子类方法返回更具体的类型。
兼容性规则的查找途径
- MyPy官方文档:在「运算符重载」章节中,明确了配对运算符的签名一致性要求,以及子类重载父类方法的Liskov替换原则。
- PEP规范:PEP 484(类型提示)和PEP 544(结构化子类型)定义了泛型类和运算符注解的基础规则,其中原地运算符(如
__ior__)的返回类型应该是Self类型(Python 3.11+支持,低版本可以用类名字符串)。
内容的提问来源于stack exchange,提问作者Gloizen
相关产品推荐
相关产品推荐

