PEP 604: I've been asked to take over as PEP-Delegate (#1538)

Here I'm also retargeting this to Python 3.10 and proposing various
small changes to clarify the PEP. In addition, we have a new reference
implementation in the works.
This commit is contained in:
Guido van Rossum 2020-07-28 09:02:01 -07:00 committed by GitHub
parent b1c63f8f00
commit fede4ec838
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 46 additions and 30 deletions

View File

@ -1,50 +1,61 @@
PEP: 604
Title: Complementary syntax for ``Union[]``
Title: Allow writing union types as ``X | Y``
Author: Philippe PRADOS <python@prados.fr>
Sponsor: Chris Angelico <rosuav@gmail.com>
BDFL-Delegate: Ivan Levkivskyi <levkivskyi@gmail.com>
BDFL-Delegate: Guido van Rossum <guido@python.org>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 28-Aug-2019
Python-Version: 3.9
Python-Version: 3.10
Abstract
========
This PEP proposes a complementary syntax for ``Union[X,Y]`` and extends its
purpose to ``isinstance`` and ``issubclass``.
This PEP proposes overloading the ``|`` operator on types to allow
writing ``Union[X, Y]`` as ``X | Y``, and allows it to appear in
``isinstance`` and ``issubclass`` calls.
Motivation
==========
PEP 484 and PEP 526 propose a generic syntax to add typing to variables,
parameters and function returns. PEP 585 proposes to
`expose parameters to generics at runtime <https://www.python.org/dev/peps/pep-0585/#id7>`_.
MyPy [1]_ accepts a syntax which looks like::
parameters and function returns. PEP 585 proposes to `expose
parameters to generics at runtime
<https://www.python.org/dev/peps/pep-0585/#parameters-to-generics-are-available-at-runtime>`_.
Mypy [1]_ accepts a syntax which looks like::
annotation: name_type
name_type: NAME (args)?
args: '[' paramslist ']'
paramslist: annotation (',' annotation)* [',']
- To describe a disjunction, the user must use ``Union[X,Y]``.
- To describe a disjunction (union type), the user must use ``Union[X, Y]``.
The verbosity of this syntax does not help the adoption.
Proposal
========
Inspired by Scala language [2]_ and Pike [3]_, this proposal adds operator ``type.__or__()``.
With this new operator, it is possible to write ``int | str`` instead of
``Union[int,str]``. The result of this expression would then be valid in
Inspired by Scala [2]_ and Pike [3]_, this proposal adds operator
``type.__or__()``. With this new operator, it is possible to write
``int | str`` instead of ``Union[int, str]``. In addition to
annotations, the result of this expression would then be valid in
``isinstance()`` and ``issubclass()``::
isinstance(5, int | str)
issubclass(bool, int | float)
We will also be able to write ``t | None`` or ``None | t`` instead of
``Optional[t]``::
isinstance(None, int | None)
isinstance(42, None | int)
Examples
========
@ -52,7 +63,7 @@ Here are some examples of what we can do with this feature.
::
# in place of
# 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
@ -65,35 +76,40 @@ Here are some examples of what we can do with this feature.
assert isinstance("", int | str)
assert issubclass(bool, int | float)
Once the Python language is extended, MyPy [1]_ and other type checkers will
Once the Python language is extended, mypy [1]_ and other type checkers will
need to be updated to accept this new syntax.
Technical point of view
=======================
To accept to extend ``isinstance()`` and ``issubclass()``, the object ``_GenericAlias`` must be available as a core,
that doesn't have a directly-accessible name but via alias in typing module..
Implementation
==============
A new built-in ``Union`` type must be implemented to hold the return
value of ``t1 | t2``, and it must be supported by ``isinstance()`` and
``issubclass()``. This type can be placed in the ``types`` module.
Interoperability between ``types.Union`` and ``typing.Union`` must be
provided.
Incompatible changes
====================
In some situations, some exceptions will not be raised as expected.
For backward compatibility, ``typing.py`` must say ``_GenericAlias = _GenericAlias``.
If a metaclass implements the ``__or__`` operator, it will override this::
>>> class M(type):
... def __or__(self,other): return "Hello"
... def __or__(self, other): return "Hello"
...
>>> class C(metaclass=M):pass
>>> class C(metaclass=M): pass
...
>>> C | int
'Hello'
>>> int | C
typing.Union[int, __main__.C]
>>> Union[C,int]
>>> Union[C, int]
typing.Union[__main__.C, int]
Objections and responses
========================
@ -120,8 +136,8 @@ CONS:
Pytype, and who knows what else (it's a minor change see "Reference Implementation"
Change only the PEP 484 (Type hints) to accept the syntax ``type1 | type2`` ?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Change only PEP 484 (Type hints) to accept the syntax ``type1 | type2`` ?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PEP 563 (Postponed Evaluation of Annotations) is enough to accept this proposition,
if we accept to not be compatible with the dynamic evaluation of annotations (``eval()``).
@ -157,16 +173,16 @@ CONS:
Reference Implementation
========================
A proposed implementation for `cpython is here
<https://github.com/pprados/cpython/tree/PEP604>`_.
A proposed implementation for `mypy is here
<https://github.com/pprados/mypy/tree/PEP604>`_.
- A proposed implementation for `cpython is here
<https://github.com/python/cpython/pull/21515>`_.
- A proposed implementation for `mypy is here
<https://github.com/pprados/mypy/tree/PEP604>`_.
References
==========
.. [1] MyPy
.. [1] mypy
http://mypy-lang.org/
.. [2] Scala Union Types
https://dotty.epfl.ch/docs/reference/new-types/union-types.html