如何让mypy不将自定义类视为object的子类型并限制类型兼容?
如何让mypy禁止自定义类与非同类实例比较,并阻止其传入期望object的函数?
一、禁止MyClass与非同类实例的==比较(触发mypy类型错误)
要让MyClass(5) == 5触发mypy报错,需要通过精确的__eq__类型签名配合mypy的严格模式实现:
- 使用
@overload为__eq__定义仅接受Self类型的类型签名,同时保留运行时对object参数的兼容处理:
from typing import Self, overload class MyClass: def __init__(self: Self, x: int): self.x = x # 类型检查层面:仅允许与MyClass实例比较 @overload def __eq__(self: Self, other: Self) -> bool: ... # 运行时层面:处理所有object类型,非同类返回NotImplemented def __eq__(self: Self, other: object) -> bool: if not isinstance(other, MyClass): return NotImplemented return self.x == other.x
- 开启mypy的严格检查模式:
运行mypy时添加--strict-equality和--strict(或至少--disallow-untyped-defs)参数:
mypy test.py --strict-equality --strict
此时mypy会对MyClass(5) == 5抛出类型错误:
error: Unsupported operand types for == ("MyClass" and "int")
二、禁止将MyClass实例传入期望object的函数
这一需求违背Python类型系统的里氏替换原则(所有自定义类都是object的子类,类型检查器默认允许子类赋值给父类类型),标准工具无法直接实现,但可通过以下方案近似达成:
方案1:修改函数参数类型为排除MyClass的自定义Protocol
定义一个排除MyClass的Protocol,配合类型守卫限制函数参数:
from typing import Protocol, TypeGuard class NotMyClass(Protocol): pass def is_not_myclass(obj: object) -> TypeGuard[NotMyClass]: return not isinstance(obj, MyClass) def abstract_comparator(a: NotMyClass, b: NotMyClass) -> bool: # 可选:运行时补充断言检查 assert is_not_myclass(a) and is_not_myclass(b) return a == b
此时传入MyClass实例会触发mypy类型错误,但需要修改目标函数的类型注解。
方案2:运行时检查补充
如果无法修改函数类型注解,可在函数内部添加运行时类型检查,作为类型检查的兜底:
def abstract_comparator(a: object, b: object) -> bool: if isinstance(a, MyClass) or isinstance(b, MyClass): raise TypeError("MyClass instances are not allowed in this function") return a == b
方案3:自定义mypy插件
通过编写mypy插件实现自定义类型检查规则,拦截MyClass实例赋值给object类型参数的场景。这需要熟悉mypy的插件开发API,适合复杂场景的长期维护。
内容的提问来源于stack exchange,提问作者Филя Усков
相关产品推荐
相关产品推荐

