Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 718 – 可下标的函数

作者:
James Hilton-Balfe <gobot1234yt at gmail.com>
发起人:
Guido van Rossum <guido at python.org>
讨论至:
Discourse 帖子
状态:
草案
类型:
标准跟踪
主题:
类型标注
创建日期:
2023年6月23日
Python 版本:
3.15
发布历史:
2023年6月24日

目录

摘要

本 PEP 提议使函数对象可用于类型标注的下标操作。这样做可以使开发者明确控制类型检查器生成的类型,尤其是在双向推断(允许推断匿名函数的参数类型)和其他非特殊化方法不足的情况下。它也使函数在可下标性方面与常规类保持一致。

动机

未知类型

目前,在某些情况下无法推断泛型函数的类型参数。

def make_list[T](*args: T) -> list[T]: ...
reveal_type(make_list())  # type checker cannot infer a meaningful type for T

使 FunctionType 的实例可下标将允许此构造函数被类型化。

reveal_type(make_list[int]())  # type is list[int]

目前,你必须使用赋值来提供精确的类型。

x: list[int] = make_list()
reveal_type(x)  # type is list[int]

但这段代码对于一个简单的函数调用来说不必要地冗长,占用了多行。

类似地,此示例中的 T 目前无法有意义地推断,因此在没有额外赋值的情况下 x 是无类型的。

def factory[T](func: Callable[[T], Any]) -> Foo[T]: ...

reveal_type(factory(lambda x: "Hello World" * x))

然而,如果函数对象是可下标的,则可以给定更具体的类型。

reveal_type(factory[int](lambda x: "Hello World" * x))  # type is Foo[int]

不可判定的类型推断

甚至在子类关系导致类型推断不可能的情况下。但是,如果你可以专门化函数,类型检查器就可以推断出有意义的类型。

def foo[T](x: Sequence[T] | T) -> list[T]: ...

reveal_type(foo[bytes](b"hello"))

目前,类型检查器在这里不能始终一致地合成类型。

不可解的类型参数

目前,对于非专门化的字面量,无法确定类似以下情况的类型:

def foo[T](x: list[T]) -> T: ...
reveal_type(foo([]))  # type checker cannot infer T (yet again)
reveal_type(foo[int]([]))  # type is int

能够预先指定必须将特定类型传递给函数的情况也很有用。

words = ["hello", "world"]
foo[int](words)  # Invalid: list[str] is incompatible with list[int]

允许下标操作使函数和方法与泛型类保持一致,而之前它们并不一致。虽然所有提议的更改都可以通过可调用的泛型类实现,但语法糖将非常受欢迎。

因此,专门化函数并将其用作新工厂是可行的。

make_int_list = make_list[int]
reveal_type(make_int_list())  # type is list[int]

单态化和具体化

此提案还为 单态化具体化类型 打开了大门。

这将实现一个据称已被多次请求的功能。

请注意,此功能并非由本 PEP 提出,但将来可能会实现。

此类功能的语法可能类似于:

def foo[T]():
   return T.__value__

assert foo[int]() is int

基本原理

本 PEP 中的函数对象指的是 FunctionTypeMethodTypeBuiltinFunctionTypeBuiltinMethodTypeMethodWrapperType

对于 MethodType,你应该能够这样写:

class Foo:
    def make_list[T](self, *args: T) -> list[T]: ...

Foo().make_list[int]()

并使其与 FunctionType 类似地工作。

对于 BuiltinFunctionType,泛型内置函数(例如 maxmin)将像 Python 中定义的函数一样工作。内置函数应尽可能像 Python 中实现的函数一样。

BuiltinMethodTypeBuiltinFunctionType 类型相同。

MethodWrapperType(例如 object().__str__ 的类型)对于泛型魔术方法很有用。

规范

函数对象应该实现 __getitem__ 以允许在运行时进行下标操作,并返回一个 types.GenericAlias 实例,其中 __origin__ 设置为可调用对象,__args__ 设置为传递的类型。

类型检查器应支持函数下标,并理解传递给函数下标的参数应遵循与泛型可调用类相同的规则。

设置 __orig_class__

目前,__orig_class__ 是在 GenericAlias.__call__ 中设置的一个属性,指向创建被调用类的 GenericAlias 实例,例如:

class Foo[T]: ...

assert Foo[int]().__orig_class__ == Foo[int]

目前,__orig_class__ 是无条件设置的;但是,为了避免在任何创建的实例上可能发生擦除,如果 __origin__ 是任何函数对象的实例,则不应设置此属性。

如果没有此更改,以下代码片段在运行时将失败,因为 __orig_class__ 将是 bar[str] 而不是 Foo[int]

def bar[U]():
    return Foo[int]()

assert bar[str]().__orig_class__  == Foo[int]

@typing.overload 的交互

重载函数应该像以前一样工作,因为它们对运行时类型没有影响。唯一的改变是更多情况将变得可判定,并且行为/重载可以由开发者指定,而不是留给重载/联合的顺序决定。

向后兼容性

目前这些类不可子类化,因此对于已经实现 __getitem__ 的类,没有向后兼容性问题。

参考实现

所提议的运行时更改可以在此处找到 https://github.com/Gobot1234/cpython/tree/function-subscript

致谢

感谢 Alex Waygood 和 Jelle Zijlstra 对本 PEP 的反馈,以及 Guido 提供的一些启发性示例。


来源: https://github.com/python/peps/blob/main/peps/pep-0718.rst

最后修改: 2025-05-06 20:54:28 GMT