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

Python运算符dunder方法类型注解的最佳实践是什么

问题背景

假设定义了一个支持+运算的类A,A的实例之间相加后返回的结果仍然是A的实例,示例代码如下:

class A:
    ...

    def __add__(self, other):
        if not isinstance(other, A):
            return NotImplemented
        else:
            return A(self._value + other._value)  # 具体实现逻辑不影响讨论

给这个__add__方法加类型注解时,目前能想到的三种方案都存在明显缺陷:

公共前置说明

为了简化示例,下面的演示统一使用Python 3.11引入的Self类型,这个选择不影响核心问题的讨论,前置导入代码如下:

from types import NotImplementedType
from typing import Any, Self, overload

方案1

def __add__(self, other: Self) -> Self:

对这个方案的顾虑:

  • 方法实际可能返回NotImplemented,返回值只标Self好像不完整
  • 正如Richard Ambler在评论中提到的,other参数实际可能接收任意类型的对象,直接标Self好像不符合实际情况

方案2

def __add__(self, other: Any) -> Self | NotImplementedType:

这个方案的问题很明确:类型检查器会把A() + A()的结果推断为A | NotImplementedType类型,后面写类似obj = A() + A(); f(obj)的代码时,只要f要求传入A类型,就会触发*“obj可能是NotImplemented”*的无意义类型警告。

方案3

@overload
def __add__(self, other: A) -> Self: ...
@overload
def __add__(self, other: Any) -> Self | NotImplementedType: ...

def __add__(self, other):
   ...  # 具体实现

这个方案逻辑上看似没问题,但实际用起来很麻烦:

  • 旧版本mypy对Self类型的支持不完善,没法正常做类型检查
  • 需要写很多冗余的样板代码,运算符类型标注本来是非常通用的常规需求,写这么多代码性价比太低

官方最佳实践

直接用方案1的写法就对了,这也是Python官方typeshed仓库为所有内置类型运算符方法采用的标注标准。所有主流类型检查器(mypy 1.0+、pyright、pytype)都对二元运算的双下方法做了特殊适配,根本不需要你额外处理NotImplemented的类型逻辑:

  • 类型检查器默认知道__add__这类运算符方法可能返回NotImplemented,不会因为你没在返回类型里写NotImplementedType就报类型错误。当你写a + b这样的表达式时,检查器会自动处理NotImplemented分支:如果b的类型和__add__标注的入参类型不匹配,会自动尝试调用b的反向运算方法__radd__,只有两边都返回NotImplemented时,才会判定这个运算表达式存在类型错误。
  • 不需要把other参数标成Any,如果你的类只支持和同类型实例相加,直接标Self就可以。类型检查器不会因为传入其他类型的对象就直接报错,它会完全遵循Python原生的运算符分派逻辑做校验,不会干扰正常的运行时行为。
  • 只有当你的类需要支持和多种不同类型对象做相加运算时(比如既支持和A实例相加,也支持和整数相加),才需要用到@overload,分别给每种支持的入参类型标注对应的返回值就行,不需要额外加一个入参为Any、返回Self | NotImplementedType的重载分支。

如果要兼容不支持Self类型的旧版类型检查器,可以用绑定到当前类的TypeVar实现完全等价的效果,示例如下:

from typing import TypeVar

T = TypeVar("T", bound="A")

class A:
    ...
    def __add__(self: T, other: T) -> T:
        if not isinstance(other, A):
            return NotImplemented
        else:
            return A(self._value + other._value)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 07:54:21