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

如何用Python类型标注定义支持自身减法的TypeVar?

解决方案:实现类型T要求T.__sub__(self, other: T) -> T

当前Python类型系统(结合Pyright)可以实现你的需求,以下是具体解决步骤和问题应对:

一、正确定义泛型SupportsSub协议

不能直接将TypeVar绑定到泛型协议,但可以利用**Self类型(Python 3.11+)**或递归TypeVar来定义符合要求的协议:

方式1:使用Python 3.11+的Self类型

from typing import Protocol, Self, TypeVar, Generic

class SupportsSub(Protocol):
    def __sub__(self, other: Self) -> Self: ...

# 定义绑定到该协议的TypeVar
T = TypeVar('T', bound=SupportsSub)

方式2:兼容旧版本的递归TypeVar绑定

from typing import Protocol, TypeVar, Generic

T = TypeVar('T')

class SupportsSub(Protocol[T]):
    def __sub__(self, other: T) -> T: ...

# 重新定义绑定到泛型协议的TypeVar
SubT = TypeVar('SubT', bound=SupportsSub['SubT'])

这种定义下,只有实现了__sub__且参数、返回值均为自身类型的类,才会被视为符合SupportsSub协议,解决了你提到的「允许T与int相减」的问题(非T类型的参数会触发类型检查报错)。

二、解决内置类型(如float)的兼容问题

Pyright支持结构子类型匹配,内置类型如float、int的__sub__签名完全符合SupportsSub协议的要求(接受同类型参数并返回同类型),因此无需显式让内置类型继承协议。如果Pyright仍报错,可通过以下方式兼容:

  1. 确保Pyright版本为最新(旧版本可能对内置类型的协议匹配支持不全);
  2. 若仍有问题,可在泛型类中显式兼容内置类型:
from typing import Union

# 扩展TypeVar的绑定范围
T = TypeVar('T', bound=Union[SupportsSub, int, float, complex])

class Consumption(Generic[T]):
    def __init__(self, value: T):
        self.value = value
    
    def subtract(self, other: 'Consumption[T]') -> 'Consumption[T]':
        # 此时self.value - other.value会被Pyright正确识别为合法操作
        return Consumption(self.value - other.value)

三、Pyright误判QDate不支持<运算符的处理

这属于Pyright对PySide库的类型标注支持不全问题,可通过以下方式解决:

  • 升级Pyright到最新稳定版,官方后续版本可能已修复该类型识别问题;
  • 自定义补充QDate的协议:
from typing import Protocol
from PySide6.QtCore import QDate

class SupportsLt(Protocol):
    def __lt__(self, other: QDate) -> bool: ...

# 显式告知Pyright QDate符合该协议
QDate.__lt__ = lambda self, other: self.__lt__(other)  # 仅用于类型提示,不影响运行
  • 临时用# type: ignore跳过单条语句的类型检查(不推荐长期使用)。

四、验证泛型类Consumption的正确性

按照上述定义后,以下代码不会触发Pyright报错:

# 自定义符合协议的类
class MyNumber:
    def __init__(self, val: float):
        self.val = val
    def __sub__(self, other: 'MyNumber') -> 'MyNumber':
        return MyNumber(self.val - other.val)

# 使用自定义类
c1 = Consumption(MyNumber(10.0))
c2 = Consumption(MyNumber(3.0))
c3 = c1.subtract(c2)  # 类型为Consumption[MyNumber],无报错

# 使用内置float类型
c_float1 = Consumption(5.5)
c_float2 = Consumption(2.3)
c_float3 = c_float1.subtract(c_float2)  # 类型为Consumption[float],无报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 16:25:35