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

能否创建自定义类型提示生成函数?单位维度校验需求

解决方案:类型检查阶段的维度兼容性检测工具实现

一、先解决初始示例的语法问题

Python类型注解规范(PEP 484及后续)不允许函数调用语法(如f(T))作为返回类型,mypy的提示是合规的。你需要把f定义为符合规范的类型别名或泛型结构:

from typing import TypeVar, Generic, TypeAlias

T = TypeVar('T')

# 用类型别名替代函数调用,明确返回类型
f: TypeAlias = float

class C(Generic[T]):
    def g(self) -> f:
        return 1.0

a = C().g()
# 此时mypy和PyCharm都能正确识别a的类型为float

如果你的实际需求是f根据输入类型返回不同类型,可以用泛型Callable或自定义泛型类,但核心必须遵循[]的泛型语法规范。


二、核心需求:实现维度自动合并的类型检查

要让类型检查器解析DimMultiply[T, O]并自动合并维度,必须扩展类型检查器的行为——标准mypy/PyCharm不会自动处理自定义的类型运算逻辑。以下是具体实现方案:

1. 定义基础维度和操作标记

先明确维度类型和除法标记,用空类作为标识:

from typing import TypeVar, Tuple, Generic

# 基础维度类型
class Length: pass
class Time: pass
class Mass: pass

# 除法标记,用于区分分子/分母维度
class DIVIDE: pass

# 维度元组的类型变量
D = TypeVar('D', bound=Tuple)
D1 = TypeVar('D1', bound=Tuple)
D2 = TypeVar('D2', bound=Tuple)

2. 编写mypy插件实现维度合并逻辑

mypy允许通过插件扩展类型解析能力,你需要编写一个插件来处理DimMultiply和DimDivide的类型展开:

from mypy.plugin import Plugin
from mypy.types import Type, Instance, TupleType
from collections import defaultdict

class DimensionPlugin(Plugin):
    def get_generic_type_alias_expander(self, fullname: str):
        # 处理DimMultiply的类型展开
        if fullname == "your_module.DimMultiply":
            def expand_multiply(args: list[Type]) -> Type:
                # 提取两个维度元组的内容
                dims1 = args[0].items if isinstance(args[0], TupleType) else []
                dims2 = args[1].items if isinstance(args[1], TupleType) else []
                
                # 统计每个维度的净次数(分子+1,分母-1)
                dim_count = defaultdict(int)
                is_denominator = False
                for dim in dims1 + dims2:
                    if isinstance(dim, Instance) and dim.type.fullname == "your_module.DIVIDE":
                        is_denominator = True
                        continue
                    if is_denominator:
                        dim_count[dim] -= 1
                        is_denominator = False
                    else:
                        dim_count[dim] += 1
                
                # 重新构建合并后的维度元组
                merged_dims = []
                for dim, cnt in dim_count.items():
                    if cnt > 0:
                        merged_dims.extend([dim] * cnt)
                    elif cnt < 0:
                        merged_dims.append(Instance(type=self.lookup_typeinfo("your_module.DIVIDE"), args=[]))
                        merged_dims.extend([dim] * (-cnt))
                
                # 返回合并后的Tuple类型
                tuple_typeinfo = self.lookup_typeinfo("builtins.tuple")
                return TupleType(items=merged_dims, fallback=tuple_typeinfo)
            
            return expand_multiply
        
        # 处理DimDivide:等价于乘以带DIVIDE的目标维度
        elif fullname == "your_module.DimDivide":
            def expand_divide(args: list[Type]) -> Type:
                divide_mark = Instance(type=self.lookup_typeinfo("your_module.DIVIDE"), args=[])
                # 把除数维度转为"DIVIDE + 维度"的形式,复用乘法逻辑
                denominator_dims = TupleType(
                    items=[divide_mark] + args[1].items,
                    fallback=self.lookup_typeinfo("builtins.tuple")
                )
                return expand_multiply([args[0], denominator_dims])
            
            return expand_divide
        
        return None

# 插件入口
def plugin(version: str):
    return DimensionPlugin

然后在mypy.ini中启用插件:

[mypy]
plugins = path/to/your_dimension_plugin.py

3. 定义带维度的Value类

现在可以编写Value类,用泛型参数表示维度,并重写运算方法的返回类型:

from typing import Generic, TypeVar

V = TypeVar('V', bound='Value')

class Value(Generic[D]):
    def __mul__(self: Value[D1], other: Value[D2]) -> Value[DimMultiply[D1, D2]]:
        # 运行时逻辑:实际计算值和维度
        return Value()
    
    def __truediv__(self: Value[D1], other: Value[D2]) -> Value[DimDivide[D1, D2]]:
        return Value()
    
    def __add__(self: Value[D], other: Value[D]) -> Value[D]:
        # 只有相同维度才能加减,类型检查器会自动验证
        return Value()
    
    def __sub__(self: Value[D], other: Value[D]) -> Value[D]:
        return Value()

# 定义具体单位
ft = Value[Tuple[Length]]()
lbs = Value[Tuple[Mass]]()

4. 测试类型检查

现在编写测试代码,mypy会自动检测维度不兼容的加减操作:

x = 1 * ft
y = 3 * lbs
z = x + y  # mypy会抛出类型错误:Value[Tuple[Length]] 和 Value[Tuple[Mass]] 不兼容

三、PyCharm的支持方案

PyCharm对自定义类型插件的支持不如mypy灵活,你可以通过以下方式适配:

  1. 显式类型别名:提前定义常用的合并维度类型,比如LengthOverTime = DimDivide[Tuple[Length], Tuple[Time]],PyCharm会识别这些别名。
  2. 利用泛型推断:PyCharm能识别简单的泛型参数传递,复杂的维度合并可以通过类型别名辅助。
  3. 双工具配合:先用mypy作为主要类型检查工具,PyCharm负责日常编码的基础类型提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 06:28:14