如何为返回函数包装器的Python函数标注正确类型?
make_run_in_executor函数的返回类型? 我正尝试为下方的Python函数标注返回值类型:
from concurrent.futures import Executor from typing import Callable from typing_extensions import Concatenate, ParamSpec, TypeVar _P = ParamSpec("_P") _R = TypeVar("_R") def make_run_in_executor(executor: Executor): # 此处应标注什么类型? def run_in_executor( func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs ) -> _R: return executor.submit(func, *args, **kwargs).result() return run_in_executor
我尝试使用Callable[Concatenate[Callable[_P, _R], _P], _R]作为返回类型,但Pylance给出了以下错误:
Expression of type "(func: (**_P@run_in_executor) -> _R@run_in_executor, *args: _P.args, kwargs: _P.kwargs) -> _R@run_in_executor" cannot be assigned to return type "((_P@make_run_in_executor) -> _R@make_run_in_executor, _P@make_run_in_executor) -> _R@make_run_in_executor"
Type "(func: (_P@run_in_executor) -> _R@run_in_executor, *args: _P.args, kwargs: _P.kwargs) -> _R@run_in_executor" cannot be assigned to type "((_P@make_run_in_executor) -> _R@make_run_in_executor, **_P@make_run_in_executor) -> _R@make_run_in_executor"
请问是我的标注有误,还是这是Pylance的bug?或者有其他正确的类型标注方式?
你的标注有误,不是Pylance的bug。问题出在泛型参数的作用域和Concatenate的使用逻辑上:你定义的_P和_R是全局级的泛型参数,但内部的run_in_executor是一个泛型函数——每次调用它时,都可以接受不同签名的func和对应参数,所以需要让返回类型明确描述这个嵌套泛型函数的结构。
正确标注方式1:使用Protocol定义返回函数类型
用Protocol可以清晰描述嵌套函数的调用签名,类型检查器能准确识别:
from concurrent.futures import Executor from typing import Callable, TypeVar, ParamSpec, Protocol _P = ParamSpec("_P") _R = TypeVar("_R") class RunInExecutor(Protocol[_P, _R]): def __call__(self, func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... def make_run_in_executor(executor: Executor) -> RunInExecutor[_P, _R]: def run_in_executor( func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs ) -> _R: return executor.submit(func, *args, **kwargs).result() return run_in_executor
正确标注方式2:直接用Callable结合泛型参数
如果不想定义Protocol,可以直接用Callable配合Concatenate,确保泛型参数对应嵌套函数的签名:
from concurrent.futures import Executor from typing import Callable, TypeVar, ParamSpec from typing_extensions import Concatenate _P = ParamSpec("_P") _R = TypeVar("_R") def make_run_in_executor(executor: Executor) -> Callable[Concatenate[Callable[_P, _R], _P], _R]: def run_in_executor( func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs ) -> _R: return executor.submit(func, *args, **kwargs).result() return run_in_executor
错误原因说明
你之前的标注报错,是因为类型检查器把外部的_P和_R当成了make_run_in_executor的固定泛型参数,但实际上返回的run_in_executor本身是泛型的——它的_P和_R是每次调用时才确定的,和make_run_in_executor的调用无关。用Protocol或者明确的泛型Callable标注,就能让类型检查器正确识别嵌套函数的泛型特性。
内容的提问来源于stack exchange,提问作者Zizheng Tai

