Mypy技巧:如何为返回类型依赖于参数类型的函数添加类型注解
解决Mypy中多场景除法的类型注解问题
当然可以!Mypy支持通过@overload装饰器来为同一个函数定义多个不同的签名,刚好能解决你这种除法操作的双场景类型注解需求。下面是具体的实现方案:
步骤1:导入必要工具
首先需要从typing模块(Python 3.8+)或者typing_extensions(兼容更早Python版本)导入overload和Union:
from typing import overload, Union from enum import Enum from pydantic import BaseModel
步骤2:为__truediv__定义重载签名
在实现实际的除法方法前,先通过@overload为两种场景分别定义类型签名——注意重载的函数只需要写类型注解,不需要实现逻辑,实际逻辑统一写在最后一个无@overload装饰的函数里:
class MemoryUnit(Enum): """Units of memory.""" GB = 'GB' TB = 'TB' PB = 'PB' class Memory(BaseModel): """Normalized amount of memory.""" amount: int unit: MemoryUnit def __add__(self, other: 'Memory') -> 'Memory': # 示例加法逻辑:需保证单位一致 if self.unit != other.unit: raise ValueError("Units must match for addition") return Memory(amount=self.amount + other.amount, unit=self.unit) def __sub__(self, other: 'Memory') -> 'Memory': # 示例减法逻辑:需保证单位一致 if self.unit != other.unit: raise ValueError("Units must match for subtraction") return Memory(amount=self.amount - other.amount, unit=self.unit) def __mul__(self, other: int) -> 'Memory': # 示例乘法逻辑 return Memory(amount=self.amount * other, unit=self.unit) # 重载场景1:Memory 除以 Memory,返回比例值float @overload def __truediv__(self, other: 'Memory') -> float: ... # 重载场景2:Memory 除以 int,返回拆分后的Memory实例 @overload def __truediv__(self, other: int) -> 'Memory': ... # 实际实现除法逻辑,处理两种输入类型 def __truediv__(self, other: Union['Memory', int]) -> Union['Memory', float]: if isinstance(other, Memory): if self.unit != other.unit: # 实际项目中可添加单位转换逻辑,示例简化为抛出异常 raise ValueError("Units must match for ratio calculation") return self.amount / other.amount elif isinstance(other, int): # 这里根据需求选择浮点除法或整数除法,示例用整数除法 return Memory(amount=self.amount // other, unit=self.unit) else: raise TypeError(f"Unsupported division with type: {type(other)}")
关键说明
- Mypy会根据调用时传入的参数类型,自动匹配对应的重载签名,正确推断返回类型:
mem1 = Memory(amount=10, unit=MemoryUnit.GB) mem2 = Memory(amount=5, unit=MemoryUnit.GB) ratio = mem1 / mem2 # Mypy会推断ratio为float类型 split_mem = mem1 / 2 # Mypy会推断split_mem为Memory类型 - 实际实现中记得处理单位不匹配的情况,比如两个Memory实例单位不同时,可添加单位转换逻辑(比如把GB转TB后再计算),避免直接抛出异常。
内容的提问来源于stack exchange,提问作者zefciu




