如何用Python 3.12语法为偏应用实现类型标注?
自定义simple_partial类的Python 3.12类型标注方案
问题背景
需要为自定义的偏应用类simple_partial添加类型标注,优先使用Python 3.12语法。该类实现了类似functools.partial的功能,但之前的两种标注方案存在问题:
- 方案1:未区分
__init__与__call__的参数差异,类型检查无法识别偏应用的参数拆分逻辑。 - 方案2:尝试使用两个
ParamSpec但因Concatenate用法错误失效(旧写法不支持直接拼接多个ParamSpec)。
正确的类型标注实现
利用Python 3.12对ParamSpec和Concatenate的增强支持,可准确标注simple_partial的参数传递逻辑:
from typing import Callable, ParamSpec, TypeVar, Concatenate, Unpack T = TypeVar('T') PInit = ParamSpec('PInit') PCall = ParamSpec('PCall') class simple_partial[T, **PInit, **PCall]: def __init__( self, f: Callable[Concatenate[Unpack[PInit], Unpack[PCall]], T], *args: Unpack[PInit.args], **kwargs: Unpack[PInit.kwargs] ) -> None: self.f = f self.args = args self.kwargs = kwargs def __call__( self, *args: Unpack[PCall.args], **kwargs: Unpack[PCall.kwargs] ) -> T: return self.f(*(*self.args, *args), **(self.kwargs | kwargs)) # 测试代码 partially = simple_partial(lambda x, y, z=3: (x, y, z), 1) partially(2) # 类型检查会提示接受y参数和可选z参数,返回tuple[int, int, int]
方案说明
- 类型变量定义:
T:表示原函数的返回值类型。PInit:表示__init__时传入的部分参数的参数规格。PCall:表示__call__时传入的剩余参数的参数规格。
- 原函数类型约束:
- 原函数
f的参数是PInit和PCall的拼接,通过Concatenate[Unpack[PInit], Unpack[PCall]]实现参数序列的合并。
- 原函数
- 方法参数标注:
__init__接收PInit对应的位置参数和关键字参数,用Unpack展开参数规格。__call__接收PCall对应的剩余参数,同样用Unpack展开,确保类型检查能识别偏应用后需要传入的参数。
之前方案失效原因
- 方案1:使用单一
ParamSpec导致__init__和__call__都要求传入完整的原函数参数,违背了偏应用“拆分参数传递”的核心逻辑。 - 方案2:错误地直接将两个
ParamSpec传入Concatenate,正确写法需用Unpack分别展开参数规格后再拼接,该特性在Python 3.12中才支持。
内容的提问来源于stack exchange,提问作者lupl
相关产品推荐
相关产品推荐

