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

如何编写适配右操作数算术实现的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:47:06