用于相同通用函数签名的Python类型注释

2024-10-01 17:42:06 发布

您现在位置:Python中文网/ 问答频道 /正文

typing.Callable接受两个“参数”:参数类型和返回类型。对于任意参数,参数类型应该是...,或者是显式类型列表(例如[str, str, int]

有没有一种方法可以表示Callable的泛型签名完全相同(尽管是任意的)

例如,假设我想要一个接受函数并返回具有相同签名的函数,如果我事先知道函数签名,我可以这样做:

def fn_combinator(*fn:Callable[[Some, Argument, Types], ReturnType]) -> Callable[[Some, Argument, Types], ReturnType]:
    ...

然而,我不知道前面的参数类型,我希望我的组合符是适当的一般。我曾希望这会奏效:

ArgT = TypeVar("ArgT")
RetT = TypeVar("RetT")
FunT = Callable[ArgT, RetT]

def fn_combinator(*fn:FunT) -> FunT:
    ...

但是,解析器(至少在Python 3.7中)不喜欢将ArgT放在第一个位置。{}是我能做的最好的了吗


Tags: 函数类型参数defsomeargumenttypesfn
1条回答
网友
1楼 · 发布于 2024-10-01 17:42:06

Python 3.10之前的版本

如果根本不需要更改函数签名,则应将FuncT定义为TypeVar

FuncT = TypeVar("FuncT", bound=Callable[..., object])

def fn_combinator(*fn: FuncT) -> FuncT:
    ...

Is there a way of representing Callables that have exactly the same, albeit arbitrary, signatures for generics?

与类型别名(例如:FuncT = Callable[..., RetT])不同,TypeVar允许类型检查器推断参数和返回值之间的依赖关系,确保函数签名完全相同

然而,这种方法是完全有限的。使用FuncT会使正确键入返回的函数变得困难(请参见this mypy issue

def fn_combinator(*fn: FuncT) -> FuncT:
    def combined_fn(*args: Any, **kwargs: Any) -> Any:
        ...

    # return combined_fn  # Won't work. `combined_fn` is not guaranteed to be `FuncT`
    return cast(FuncT, combined_fn)

由于PEP 484中引入了Callable的限制,这是我们在Python3.7中所能做的最好的了

... only a list of parameter arguments ([A, B, C]) or an ellipsis (signifying "undefined parameters") were acceptable as the first "argument" to typing.Callable. - PEP 612


Python 3.10+

幸运的是,在Python3.10中,通过typing.ParamSpec(所谓的“参数规范变量”)和typing.ConcatenatePEP 612中提出,可调用的类型注释将变得更加灵活。这扩展了Callable以支持注释更复杂的可调用项

这意味着您将能够执行以下操作:

P = ParamSpec("P")
RetT = TypeVar("RetT")

def fn_combinator(*fn: Callable[P, RetT]) -> Callable[P, RetT]:
    ...

它还允许我们在不使用cast的情况下对返回的callable进行完全类型检查:

def fn_combinator(*fn: Callable[P, RetT]) -> Callable[P, RetT]:
    def combined_fn(*args: P.args, **kwargs: P.kwargs) -> RetT:
        ...

    return combined_fn

请参阅发行说明here

相关问题 更多 >

    热门问题