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

如何为返回函数包装器的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 23:04:56