如何在Python的Generic类中实现可选TypeVar以简化装饰器类型定义?
实现带默认泛型参数的装饰器协议
解决方案:用类型别名+重载实现简洁使用
Python的Generic目前不支持直接将第二个类型变量默认绑定到第一个,但可以通过**类型别名+@overload**的组合实现你想要的简洁写法,同时完全保留Mypy的类型校验能力。
步骤1:保留基础协议定义
先维持原本的双泛型参数协议,确保类型转换类装饰器的场景依然可用:
from typing import Protocol, TypeVar, Generic, Callable, overload TIn = TypeVar('TIn', contravariant=True) TOut = TypeVar('TOut', covariant=True) class _DecoratorProtocol(Protocol, Generic[TIn, TOut]): """内部基础协议,不直接对外暴露""" def __call__(self, value: TIn) -> TOut: ...
步骤2:定义重载的类型入口
通过@overload为Decorator提供两种使用形式:一种是仅传入TIn(自动让TOut=TIn),另一种是传入两个泛型参数:
@overload def Decorator[T]() -> type[_DecoratorProtocol[T, T]]: ... @overload def Decorator[TIn, TOut]() -> type[_DecoratorProtocol[TIn, TOut]]: ... def Decorator(*args, **kwargs): # 运行时无实际逻辑,仅用于类型注解 raise NotImplementedError("Decorator仅用于类型标注,不可实例化")
步骤3:简洁使用示例
现在你可以像预期的那样简化装饰器的返回类型标注,Mypy会自动推断TOut=TIn:
IntFunction = Callable[[int, int], int] def register_operator(op: str) -> Decorator[IntFunction]: def inner(value: IntFunction) -> IntFunction: # 注册逻辑 return value return inner @register_operator("+") def add(a: int, b: int) -> int: return a + b
类型转换场景依然兼容
当需要不同的输入输出类型时,依然可以传入两个泛型参数,不影响原有功能:
StrFunction = Callable[[int, int], str] def stringify_operator(op: str) -> Decorator[IntFunction, StrFunction]: def inner(value: IntFunction) -> StrFunction: def wrapper(a: int, b: int) -> str: return f"{a} {op} {b} = {value(a, b)}" return wrapper return inner @stringify_operator("*") def multiply(a: int, b: int) -> int: return a * b # Mypy会自动校验multiply的输入是否符合IntFunction,返回是否符合StrFunction
原写法失效的原因
Python泛型类不支持将第二个类型变量的默认值设为第一个类型变量(TOut=TIn),这是当前类型系统的限制——类型变量的默认值必须是具体类型或无,不能依赖其他类型变量。
内容的提问来源于stack exchange,提问作者Maddy Guthridge
相关产品推荐
相关产品推荐

