如何编写适配右操作数算术实现的Python Protocol?
问题分析
你定义的SupportsArithmetic协议要求类必须实现__add__方法,但像Instance(实际场景中的datetime)这类无法修改的类,并没有实现对应Delta(实际是dateutil.relativedelta.relativedelta)的__add__,运算逻辑靠右操作数的__radd__完成,导致mypy判定该类不匹配协议,触发报错。
解决方案
方案1:调用时使用类型转换
在调用函数时,用typing.cast将实例强制转换为协议类型,绕过mypy的结构检查:
from typing import cast # 其余代码保持不变 add_delta(cast(SupportsArithmetic, Instance(5)))
这种方式简单直接,适合临时场景或调用次数较少的情况,但会丢失部分类型检查的严谨性。
方案2:用存根文件补充类型定义
对于无法修改的类(如datetime或示例中的Instance),创建存根文件(.pyi)补充__add__方法的类型提示,让mypy认为该类符合协议要求,同时不影响实际运行逻辑。
以示例中的Instance为例,创建instance.pyi文件:
from dataclasses import dataclass from typing import SupportsArithmetic from your_module import Delta @dataclass class Instance: value: int def __add__(self, other: Delta) -> SupportsArithmetic: ...
实际运行时Python会忽略存根文件,依然通过Delta的__radd__处理运算,但mypy会依据存根文件判定Instance符合SupportsArithmetic协议。
针对datetime场景,可创建datetime.pyi存根文件:
from datetime import datetime as _Datetime from dateutil.relativedelta import relativedelta class datetime(_Datetime): def __add__(self, other: relativedelta) -> datetime: ...
方案3:调整协议定义(兼容右操作数__radd__逻辑)
重新定义协议,允许__add__返回NotImplemented,兼容右操作数处理运算的情况:
from typing import Protocol, TypeVar T = TypeVar('T', bound='SupportsArithmetic') class SupportsArithmetic(Protocol): def __add__(self, other: "Delta") -> T | NotImplemented: ...
之后在无法修改的类的类型层面(如存根文件)补充该__add__方法的定义,让mypy认可类满足协议要求。这种方式更贴合类型系统设计,保留类型检查的严谨性。
内容的提问来源于stack exchange,提问作者Björn Pollex
相关产品推荐
相关产品推荐

