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

Python 3.10:如何为继承Generic[TypeVar]的数据类正确类型提示__add__?

如何为温度单位类的魔法方法添加正确的类型提示

我开发了一个名为totemp的温度单位转换工具包(开发阶段),包含Celsius、Fahrenheit、Delisle、Kelvin、Newton、Rankine、Réaumur和Romer共8个数据类。当前Celsius类的__add__方法用@no_type_check绕过了mypy检查,尝试过抽象类、NewType都没解决类型提示问题,想知道怎么给__add__、__gt__/__lt__这类魔法方法正确添加类型提示。

当前Celsius类核心实现:

from dataclasses import dataclass, field
from typing import Generic, TypeVar, no_type_check

TEMP = TypeVar('TEMP', int, float)  # TEMP must be int or float

@dataclass
class Celsius(Generic[TEMP]):
    """..."""

    __value: TEMP
    __symbol: str = field(compare=False, repr=False, default='ºC')

    def __str__(self) -> str:
        return f'{self.__value}{self.__symbol}'

    def __repr__(self) -> str:
        return f'Celsius({self.__value})'

    @no_type_check
    def __add__(self, other):
        match str(type(other)):
            case "<class 'totemp.temperature_types.Celsius'>":
                return Celsius(self.__value + other.value)
            case _:
                try:
                    other = other.to_celsius()
                    return Celsius(self.__value + other.value)
                except AttributeError as error:
                    print(
                        f'\033[31;1mAttributeError: {error}.\n    ' +
                        f"Cause: '{other}' is not a temperature scale.\033[m"
                    )

    def to_fahrenheit(self) -> 'Fahrenheit[TEMP]':
        fahrenheit = type(self.__value)(self.__value * 9 / 5 + 32)
        return Fahrenheit(fahrenheit)

解决方案:基于抽象基类统一类型约束

步骤1:定义抽象基类统一接口

先创建一个抽象基类,让所有温度类继承,定义通用的转换方法和公共属性访问器,给类型检查器提供明确的类型边界。

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Generic, TypeVar

# 约束数值类型为int/float
TEMP = TypeVar('TEMP', int, float)
# 约束所有温度类的类型变量,绑定到抽象基类
TempType = TypeVar('TempType', bound='Temperature')

@dataclass
class Temperature(ABC, Generic[TEMP]):
    _value: TEMP
    _symbol: str = field(compare=False, repr=False)

    @property
    def value(self) -> TEMP:
        return self._value

    @abstractmethod
    def to_celsius(self) -> 'Celsius[TEMP]':
        """转换为摄氏度的抽象方法"""
        ...

    @abstractmethod
    def __add__(self, other: TempType) -> 'Celsius[TEMP]':
        ...

    @abstractmethod
    def __gt__(self, other: TempType) -> bool:
        ...

    @abstractmethod
    def __lt__(self, other: TempType) -> bool:
        ...

步骤2:修改Celsius类继承抽象基类

移除@no_type_check,基于抽象基类的接口实现魔法方法并添加类型提示:

@dataclass
class Celsius(Temperature[TEMP]):
    _symbol: str = field(compare=False, repr=False, default='ºC')

    def __str__(self) -> str:
        return f'{self.value}{self._symbol}'

    def __repr__(self) -> str:
        return f'Celsius({self.value})'

    def __add__(self, other: TempType) -> 'Celsius[TEMP]':
        # 统一转换为摄氏度后计算
        other_celsius = other.to_celsius()
        return Celsius(self.value + other_celsius.value)

    def __gt__(self, other: TempType) -> bool:
        other_celsius = other.to_celsius()
        return self.value > other_celsius.value

    def __lt__(self, other: TempType) -> bool:
        other_celsius = other.to_celsius()
        return self.value < other_celsius.value

    def to_celsius(self) -> 'Celsius[TEMP]':
        return self

    def to_fahrenheit(self) -> 'Fahrenheit[TEMP]':
        fahrenheit_val = type(self.value)(self.value * 9 / 5 + 32)
        return Fahrenheit(fahrenheit_val)

步骤3:其他温度类的统一实现

以Fahrenheit为例,只需继承抽象基类并实现抽象方法即可,类型提示会自动适配:

@dataclass
class Fahrenheit(Temperature[TEMP]):
    _symbol: str = field(compare=False, repr=False, default='ºF')

    def __str__(self) -> str:
        return f'{self.value}{self._symbol}'

    def __repr__(self) -> str:
        return f'Fahrenheit({self.value})'

    def __add__(self, other: TempType) -> 'Celsius[TEMP]':
        other_celsius = other.to_celsius()
        self_celsius = self.to_celsius()
        return Celsius(self_celsius.value + other_celsius.value)

    def __gt__(self, other: TempType) -> bool:
        other_celsius = other.to_celsius()
        self_celsius = self.to_celsius()
        return self_celsius.value > other_celsius.value

    def __lt__(self, other: TempType) -> bool:
        other_celsius = other.to_celsius()
        self_celsius = self.to_celsius()
        return self_celsius.value < other_celsius.value

    def to_celsius(self) -> 'Celsius[TEMP]':
        celsius_val = type(self.value)((self.value - 32) * 5 / 9)
        return Celsius(celsius_val)

关键说明

  1. 类型变量绑定:通过TempType = TypeVar('TempType', bound='Temperature'),让mypy明确识别other是任意温度类实例,且拥有to_celsius等统一方法。
  2. 属性访问优化:将私有__value改为受保护的_value,并通过@property暴露value,避免类型检查器无法识别私有属性的问题。
  3. 统一逻辑:所有魔法方法基于to_celsius转换实现,既保证功能一致性,也让类型提示准确无误,无需再用@no_type_check绕过检查。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:40:26