Python如何为接收可调用对象及参数的数值积分函数添加类型标注
解决方案
你提到的PEP 612引入的ParamSpec(参数规范变量)完全可以满足需求,配合Concatenate就能精准约束func和args的类型关系,比自定义Protocol兼容性更好,也能直接适配lambda表达式。
完整类型标注实现
Python 3.10 兼容版本:
from typing import Callable, Concatenate, ParamSpec P = ParamSpec("P") def simple_quad( func: Callable[Concatenate[float, P], float], a: float, b: float, args: P.args = () ) -> float: # 原有实现逻辑 def strips(n: int): for i in range(n): x = a + (b - a) * i / n yield func(x, *args) * 1 / n return sum(strips(1000))
如果是Python 3.11及以上版本,可以用更简洁的*P写法,省略Concatenate:
from typing import Callable, ParamSpec P = ParamSpec("P") def simple_quad( func: Callable[[float, *P], float], a: float, b: float, args: tuple[*P] = () ) -> float: # 原有实现逻辑
约束逻辑说明
Concatenate[float, P]明确要求func的第一个位置参数必须是float类型,返回值固定为float,剩余位置参数的长度、类型全部由P自动捕获args的类型直接绑定P.args,也就是func除第一个参数外剩余位置参数对应的元组类型,mypy会自动校验传入的args元组是否和func的参数要求完全匹配- 对于存在必填仅限关键字参数(
*后无默认值的参数)的函数,会因为不符合Callable的签名要求,直接被mypy拦截,完全符合禁止这类函数传入的需求
校验效果
你的所有测试用例都可以被mypy正确校验:
- 传入
cubic搭配args=(1,2,3,4):int是float的子类,类型兼容,无报错 - 传入
cubic但不带args:P捕获到需要4个float参数,空元组不匹配,mypy直接报错 - 传入
cubic搭配字符串类型的args:元组类型不匹配,mypy报错 - 传入无额外参数的
x_squared:P被推断为空参数,空元组匹配,无报错 - 传入带必填仅限关键字参数的
problematic:签名不匹配,mypy直接报错
该方案不需要自定义Protocol,天然兼容lambda、普通函数、绑定方法等所有可调用对象,非常适合用来写scipy的.pyi存根文件。
内容的提问来源于stack exchange,提问作者Greedo
相关产品推荐
相关产品推荐

