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

标签 python generics type-annotation

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

有没有办法表示Callable s 具有完全相同的,尽管是任意的,泛型签名?

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

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 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)
由于 Callable 的限制,这是我们从 Python 3.7 开始所能做的最好的事情。介绍于 PEP 484 .

... 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+
幸运的是,可调用对象的类型注释在 Python 3.10 中将变得更加灵活,使用 typing.ParamSpec (所谓的“参数规范变量”)和 typing.ConcatenatePEP 612 中提出.这扩展了 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
查看发行说明 here .

关于相同的通用函数签名的 Python 类型注释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61733057/

相关文章:

python - 牛顿分形 : mathoverflow error

generics - 泛型函数可以用特征参数化吗?

ios - Swift - 编译器错误 - 段错误 11 - 泛型符合协议(protocol)

java - 如何访问接收器类型参数的类型注释

java - @EnsuresNonNullIf 注释给出 "conditional postcondition not satisfied"-警告

python - 删除没有名称的 pandas 列

python - 无法弄清楚查询失败的原因

c# - 如何传递要使用 LINQ 调用的函数委托(delegate)?

java - 如何在 Java 中使用自定义类型注解

python - 如何在官方 Windows Python 2.5 上使用 time > year 2038