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

Python 增强提案

PEP 604 – 允许将联合类型写成 X | Y

作者:
Philippe PRADOS <python at prados.fr>,Maggie Moss <maggiebmoss at gmail.com>
赞助人:
Chris Angelico <rosuav at gmail.com>
BDFL 代表:
Guido van Rossum <guido at python.org>
讨论列表:
Typing-SIG 列表
状态:
最终
类型:
标准跟踪
主题:
类型提示
创建日期:
2019 年 8 月 28 日
Python 版本:
3.10
历史记录:
2019 年 8 月 28 日,2020 年 8 月 5 日

目录

重要

此 PEP 是一个历史文档。最新的规范文档现在可以在 联合类型 中找到。

×

请参阅 PEP 1,了解如何提出更改。

摘要

此 PEP 提出对类型上的 | 运算符进行重载,以允许将 Union[X, Y] 写成 X | Y,并允许它出现在 isinstanceissubclass 调用中。

动机

PEP 484PEP 526 提出了一种通用语法,用于向变量、参数和函数返回值添加类型提示。 PEP 585 提出要 在运行时公开泛型的参数。Mypy [1] 接受一个看起来像这样的语法

annotation: name_type
name_type: NAME (args)?
args: '[' paramslist ']'
paramslist: annotation (',' annotation)* [',']
  • 要描述一个析取(联合类型),用户必须使用 Union[X, Y]

这种语法的冗长性不利于类型采用。

提案

受 Scala [2] 和 Pike [3] 的启发,此提案添加了运算符 type.__or__()。使用此新运算符,可以编写 int | str 而不是 Union[int, str]。除了注释之外,此表达式的结果在 isinstance()issubclass() 中也应该是有效的。

isinstance(5, int | str)
issubclass(bool, int | float)

我们还能够编写 t | NoneNone | t 而不是 Optional[t]

isinstance(None, int | None)
isinstance(42, None | int)

规范

新的联合语法应该被接受用于函数、变量和参数注释。

简化语法

# Instead of
# def f(list: List[Union[int, str]], param: Optional[int]) -> Union[float, str]
def f(list: List[int | str], param: int | None) -> float | str:
    pass

f([1, "abc"], None)

# Instead of typing.List[typing.Union[str, int]]
typing.List[str | int]
list[str | int]

# Instead of typing.Dict[str, typing.Union[int, float]]
typing.Dict[str, int | float]
dict[str, int | float]

现有的 typing.Union| 语法应该等价。

int | str == typing.Union[int, str]

typing.Union[int, int] == int
int | int == int

联合中的项目顺序不应该影响相等性。

(int | str) == (str | int)
(int | str | float) == typing.Union[str, float, int]

可选值应该等价于新的联合语法。

None | t == typing.Optional[t]

应该实现一个新的 Union.__repr__() 方法。

str(int | list[str])
# int | list[str]

str(int | int)
# int

isinstance 和 issubclass

只要联合项本身是 isinstanceissubclass 的有效参数,新的语法就应该被接受用于对 isinstanceissubclass 的调用。

# valid
isinstance("", int | str)

# invalid
isinstance(2, list[int]) # TypeError: isinstance() argument 2 cannot be a parameterized generic
isinstance(1, int | list[int])

# valid
issubclass(bool, int | float)

# invalid
issubclass(bool, bool | list[int])

不兼容的更改

在某些情况下,一些异常不会像预期的那样被引发。

如果元类实现了 __or__ 运算符,它将覆盖此运算符。

>>> class M(type):
...     def __or__(self, other): return "Hello"
...
>>> class C(metaclass=M): pass
...
>>> C | int
'Hello'
>>> int | C
typing.Union[int, __main__.C]
>>> Union[C, int]
typing.Union[__main__.C, int]

异议和回应

有关讨论的更多详细信息,请参阅下面的链接。

1. 为 Union[type1, type2] 添加一个新的运算符?

优点

  • 此语法更易读,并且类似于其他语言(Scala 等)。
  • 在运行时,int|str 可能会在 3.10 中返回一个简单的对象,而不是你需要从导入 typing 中获取的所有内容。

缺点

  • 添加此运算符引入了 typingbuiltins 之间的依赖关系。
  • 破坏了向后移植(因为 typing 可以轻松地进行向后移植,但核心 types 却不能)。
  • 如果 Python 本身不需要更改,我们仍然需要在 mypy、Pyre、PyCharm、Pytype 以及其他什么东西中实现它(这是一个小的更改,请参阅“参考实现”)。

2. 只更改 PEP 484(类型提示)以接受语法 type1 | type2

PEP 563(注释的延迟求值)足以接受此提议,如果我们接受不与注释的动态求值兼容 (eval())。

>>> from __future__ import annotations
>>> def foo() -> int | str: pass
...
>>> eval(foo.__annotations__['return'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'type' and 'type'

3. 将 isinstance()issubclass() 扩展为接受 Union

isinstance(x, str | int) ==> "is x an instance of str or int"

优点

  • 如果允许它们,则实例检查可以使用非常简洁的表示法。

缺点

  • 必须将所有 typing 模块迁移到 builtin 中。

参考实现

必须实现一个新的内置 Union 类型来保存 t1 | t2 的返回值,并且它必须受 isinstance()issubclass() 支持。此类型可以放在 types 模块中。必须提供 types.Uniontyping.Union 之间的互操作性。

一旦 Python 语言扩展,mypy [1] 和其他类型检查器将需要更新以接受此新语法。

参考文献


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

上次修改时间:2024-02-16 17:06:07 GMT