PEP 604 – Complementary syntax for ``Union[]``¶
- PEP
604
- Title
Complementary syntax for
Union[]- Author
Philippe PRADOS <python at prados.fr>
- Sponsor
Chris Angelico <rosuav at gmail.com>
- BDFL-Delegate
Ivan Levkivskyi <levkivskyi at gmail.com>
- Status
Draft
- Type
Standards Track
- Content-Type
- Created
28-Aug-2019
- Python-Version
3.9
Contents
Abstract¶
This PEP proposes a complementary syntax for Union[X,Y] and extends its
purpose to isinstance and issubclass.
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. 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].
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
isinstance() and issubclass():
isinstance(5, int | str)
issubclass(bool, int | float)
Examples¶
Here are some examples of what we can do with this feature.
# in place 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)
assert str | int == Union[str,int]
assert str | int | float == Union[str, int, float]
assert isinstance("", int | str)
assert issubclass(bool, int | float)
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..
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"
...
>>> class C(metaclass=M):pass
...
>>> C | int
'Hello'
>>> int | C
typing.Union[int, __main__.C]
>>> Union[C,int]
typing.Union[__main__.C, int]
Objections and responses¶
For more details about discussions, see links below:
1. Add a new operator for Union[type1|type2]?¶
PROS:
This syntax can be more readable, and is similary to others languages (Scala, …)
At runtime,
int|strmight return a simple object in 3.9, rather than everything that you’d need to grab from importingtyping
CONS:
Adding this operator introduce a dependency between
typingandbuiltinsAs breaking the backport (in that
typingcan easily be backported but coretypescan’t)If Python itself doesn’t have to be changed, we’d still need to implement it in mypy, Pyre, PyCharm, 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 ?¶
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()).
>>> 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'
2. Extend isinstance() and issubclass() to accept Union ?¶
isinstance(x, str | int) ==> "is x an instance of str or int"
PROS:
If they were permitted, then instance checking could use an extremely clean-looking notation
The implementation can use the tuple present in
Unionparameter, without create a new instance
CONS:
Must migrate all the
typingmodule inbuiltin
Reference Implementation¶
A proposed implementation for cpython is here. A proposed implementation for mypy is here.
References¶
- 1(1,2)
- 2
Scala Union Types https://dotty.epfl.ch/docs/reference/new-types/union-types.html
- 3