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

问题描述 投票:0回答:1

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
Callable[..., RetT]
是我能做的最好的事情吗?

python generics python-typing
1个回答
8
投票

Python 3.10 之前

如果您根本不需要更改函数签名,则应该将

FuncT
定义为
TypeVar
:

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

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

有没有一种方法可以表示具有完全相同(尽管是任意的)泛型签名的可调用对象?

与类型别名(例如:

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

但是,这种方法是完全有限的。使用

FuncT
会导致很难正确输入返回的函数(请参阅 this mypy issues)。

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 的限制,这是我们从 Python 3.7 起能做的最好的事情。

...只有参数参数列表([A,B,C])或省略号(表示“未定义的参数”)可以作为 Typing.Callable 的第一个“参数”。 --- PEP 612


Python 3.10+

幸运的是,可调用对象的类型注释在 Python 3.10 中变得更加灵活,具有

typing.ParamSpec
(所谓的“参数规范变量”)和
PEP 612
中提出的 typing.Concatenate。这扩展了
Callable
以支持注释更复杂的可调用对象。

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

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

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

它还允许我们在不使用

cast
:

的情况下对返回的可调用对象进行完全类型检查
def fn_combinator(*fn: Callable[P, RetT]) -> Callable[P, RetT]:
    def combined_fn(*args: P.args, **kwargs: P.kwargs) -> RetT:
        ...

    return combined_fn

请参阅发行说明此处

© www.soinside.com 2019 - 2024. All rights reserved.