Pep 0604: Remove __revert__ operator (#1183)
* Add PEP-0604 to describes an extension to Python language, which aims to add a complementary syntax to write ``Union[X,Y]`` and ``Optional[X]`` easier. * Add sample with metaclass with __invert__ and/or __or__ * Remove operator __revert__
This commit is contained in:
parent
16bc2821ee
commit
6e49cec61b
120
pep-0604.rst
120
pep-0604.rst
|
@ -13,7 +13,7 @@ Introduction
|
|||
============
|
||||
|
||||
This PEP describes an extension to Python language, which aims to add a complementary
|
||||
syntax to write ``Union[X,Y]`` and ``Optional[X]`` easier.
|
||||
syntax to write ``Union[X,Y]`` easier.
|
||||
|
||||
|
||||
Motivation
|
||||
|
@ -33,45 +33,14 @@ MyPy [4]_ accepts a syntax which looks like something like this:
|
|||
|
||||
- To describe a disjunction, the user must use ``Union[X,Y]``.
|
||||
|
||||
- To describe an optional value, the user must use ``Optional[X]``.
|
||||
|
||||
The verbosity of this syntax does not help the adoption.
|
||||
|
||||
Proposal
|
||||
========
|
||||
|
||||
Inspired by Scala language [5]_, this proposal adds two operators in the root ``type``:
|
||||
|
||||
Strong proposition
|
||||
------------------
|
||||
Add operator ``__or__()`` in the root ``type``.
|
||||
|
||||
Inspired by Scala language [5]_, this proposal adds operator ``__or__()`` in the root ``type``.
|
||||
With this new operator, it is possible to write ``int | str`` in place of ``Union[int,str]``.
|
||||
|
||||
This proposition uses the standard meaning of the ``|`` operator.
|
||||
|
||||
Optional proposition 1
|
||||
----------------------
|
||||
Add operator ``__invert__()`` in the root ``type``.
|
||||
|
||||
With this new operator, it is possible to write ``~int`` in place of ``Optional[int]``.
|
||||
|
||||
This proposition uses this operator because it is present in the language and it's conform to the
|
||||
`usage of tilde <https://www.thecut.com/article/why-the-internet-tilde-is-our-most-perfect-tool-for-snark.html>`_
|
||||
|
||||
So, the new syntax for annotations will be:
|
||||
|
||||
::
|
||||
|
||||
annotation: ( name_type | or_type | invert_type )
|
||||
or_type: name_type '|' annotation
|
||||
invert_type: '~' annotation
|
||||
name_type: NAME (args)?
|
||||
args: '[' paramslist ']'
|
||||
paramslist: annotation (',' annotation)* [',']
|
||||
|
||||
Optional proposition 2
|
||||
----------------------
|
||||
Then, it is possible to extend ``isinstance()`` and ``issubclass()``
|
||||
to accept this new syntax:
|
||||
|
||||
|
@ -88,14 +57,13 @@ 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) -> 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 ~str == Optional[str]
|
||||
|
||||
assert isinstance("", int | str)
|
||||
assert issubclass(int, int | str)
|
||||
|
@ -106,11 +74,26 @@ Incompatible changes
|
|||
====================
|
||||
In some situations, some exceptions will not be raised as expected.
|
||||
|
||||
If some metaclass overload the ``__or__`` operator, the user must resolve the ambiguities with ``Union``.
|
||||
::
|
||||
|
||||
>>> 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]
|
||||
|
||||
Dissenting Opinion
|
||||
==================
|
||||
|
||||
- `Discussion in python-ideas <https://mail.python.org/archives/list/python-ideas@python.org/thread/FCTXGDT2NNKRJQ6CDEPWUXHVG2AAQZZY/>`_
|
||||
- `Discussion in typing-sig <https://mail.python.org/archives/list/typing-sig@python.org/thread/D5HCB4NT4S3WSK33WI26WZSFEXCEMNHN/>`_
|
||||
|
||||
1. Add a new operator for ``Union[type1|type2]``?
|
||||
--------------------------------------------------
|
||||
|
@ -206,69 +189,7 @@ Use ``{int, str}`` in place of ``Union[int,str]`` ?
|
|||
- PRO: big advantage of ``{int, str}`` over ``int|str``. It doesn't require adding anything to ``type``,
|
||||
and we don't need to introduce a new lightweight builtin union type.
|
||||
|
||||
2. Add a new operator for ``Optional[type]`` ?
|
||||
----------------------------------------------
|
||||
|
||||
- CONS: ``foo | None`` is short and readable
|
||||
- CONS: ``foo | None`` it's 3 fewer characters than ``Optional[foo]``, or 30 fewer if you include the full
|
||||
removal of ``from typing import Optional``. the additional gain of ``~foo`` is only 6 characters.
|
||||
- PRO: help the readability, with a lot of parameters:
|
||||
|
||||
::
|
||||
|
||||
def f(source: str | None, destination: str | None, param: int | None):...
|
||||
def f(source: ~str, destination: ~str, param: ~int):...
|
||||
|
||||
- PRO: I'm currently working on annotating a very large codebase, and ``Optional[T]`` is so frequent that I
|
||||
think ``T | None`` would not be enough of an improvement.
|
||||
- PRO: Adding a default ``__or__`` overload to ``type`` seems a reasonable price to pay in 3.9, and
|
||||
ditto for ``__invert__``. Type checkers can support this in older Python versions using PEP 563 or in type
|
||||
comments or in "forward references" (types hidden in string literals).
|
||||
- CONS: The ``~`` is easy to be missed (at least by human readers) and the meaning not obvious.
|
||||
- PRO: Also, Python's typing system is a lot easier to grasp if you're familiar with an established modern-typed
|
||||
language (Swift, Scala, Haskell, F#, etc.), and they also use ``Optional[T]`` (or ``optional<T>`` or ``Maybe t``
|
||||
or some other spelling of the same idea) all over be place—so often that many of them have added shortcuts
|
||||
like ``T?`` to make it easier to write and less intrusive to read.
|
||||
|
||||
- if yes,
|
||||
|
||||
Add operator ``__revert__`` in type type to use syntax like ``~int`` ?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- CONS: ``~`` is not automatically readable
|
||||
|
||||
- *like ``:`` to separate variable and typing.*
|
||||
|
||||
- CONS: ``~`` means complement, which is a completely different thing from ``|None``. ``~int`` seems like it
|
||||
would actually harm comprehension instead of helping.
|
||||
- PRO: the slight abuse of ``~int`` meaning "maybe int" is pretty plausible (consider how "approximately equal"
|
||||
is written mathematically).
|
||||
- PRO: `Possibly relevant for tilde <https://www.thecut.com/article/why-the-internet-tilde-is-our-most-perfect-tool-for-snark.html>`_
|
||||
- CONS: With ``~`` there probably won't be a confusion in that sense, but someone reading it for the first time will
|
||||
definitely need to look it up (which is fine i.m.o.).
|
||||
|
||||
- *Like the first time someone reading the annotation*
|
||||
|
||||
::
|
||||
|
||||
def f(a=int):...
|
||||
def f(a:int):...
|
||||
|
||||
Add operator ``__add__`` in type type to use syntax like ``+int`` ?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- PRO: ``+foo`` definitely seems to say "foo, plus something else" to me much more than ``~foo``.
|
||||
- CONS: ``+foo`` is less intuitive than ``~foo`` for ``Optional``
|
||||
|
||||
Like Kotlin, add a new ``?`` operator to use syntax like ``int?`` or ``?int`` ?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- CONS: It's not compatible with IPython and Jupyter Lab ``?smth`` displays help for symbol ``smth``
|
||||
- CONS: With default arguments, ``?=`` looks... not great
|
||||
|
||||
::
|
||||
|
||||
def f(source: str?=def_src, destination: str?=MISSING, param: int?=1): ...
|
||||
|
||||
3. Extend ``isinstance()`` and ``issubclass()`` to accept ``Union`` ?
|
||||
2. Extend ``isinstance()`` and ``issubclass()`` to accept ``Union`` ?
|
||||
---------------------------------------------------------------------
|
||||
|
||||
::
|
||||
|
@ -277,13 +198,12 @@ Like Kotlin, add a new ``?`` operator to use syntax like ``int?`` or ``?int`` ?
|
|||
|
||||
- PRO: if they were permitted, then instance checks could use an extremely clean-looking notation for "any of these":
|
||||
- PRO: The implementation can use the tuple present in ``Union`` parameter, without create a new instance.
|
||||
- CONS: Why not accept this syntax in ``except`` ?
|
||||
|
||||
Reference Implementation
|
||||
========================
|
||||
|
||||
A proposed implementation for `cpython is here
|
||||
<https://github.com/pprados/cpython/tree/updage_isinstance>`_.
|
||||
<https://github.com/pprados/cpython/tree/update_isinstance>`_.
|
||||
A proposed implementation for `mypy is here
|
||||
<https://github.com/pprados/mypy/tree/add_INVERT_to_types>`_.
|
||||
|
||||
|
|
Loading…
Reference in New Issue