Updates following recent disscussions (#469)
This commit is contained in:
parent
170513274d
commit
2a0cc286f2
51
pep-0560.rst
51
pep-0560.rst
|
@ -18,7 +18,7 @@ the ``typing`` module are extensively used by the community, e.g. PEP 526
|
||||||
and PEP 557 extend the usage of type hints, and the backport of ``typing``
|
and PEP 557 extend the usage of type hints, and the backport of ``typing``
|
||||||
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
|
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
|
||||||
It is proposed to add two special methods ``__class_getitem__`` and
|
It is proposed to add two special methods ``__class_getitem__`` and
|
||||||
``__mro_entry__`` to the core CPython for better support of
|
``__mro_entries__`` to the core CPython for better support of
|
||||||
generic types.
|
generic types.
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,16 +135,17 @@ Note that this method is used as a fallback, so if a metaclass defines
|
||||||
``__getitem__``, then that will have the priority.
|
``__getitem__``, then that will have the priority.
|
||||||
|
|
||||||
|
|
||||||
``__mro_entry__``
|
``__mro_entries__``
|
||||||
-----------------
|
-------------------
|
||||||
|
|
||||||
If an object that is not a class object appears in the bases of a class
|
If an object that is not a class object appears in the bases of a class
|
||||||
definition, then ``__mro_entry__`` is searched on it. If found,
|
definition, then ``__mro_entries__`` is searched on it. If found,
|
||||||
it is called with the original tuple of bases as an argument. If the result
|
it is called with the original tuple of bases as an argument. The result
|
||||||
of the call is not ``None``, then it is substituted instead of this object.
|
of the call must be a tuple, that is unpacked in the bases classes in place
|
||||||
Otherwise (if the result is ``None``), the base is just removed. This is
|
of this object. (If the tuple is empty, this means that the original bases is
|
||||||
necessary to avoid inconsistent MRO errors, that are currently prevented by
|
simply discarded.) Using the method API instead of just an attribute is
|
||||||
manipulations in ``GenericMeta.__new__``. After creating the class,
|
necessary to avoid inconsistent MRO errors, and perform other manipulations
|
||||||
|
that are currently done by ``GenericMeta.__new__``. After creating the class,
|
||||||
the original bases are saved in ``__orig_bases__`` (currently this is also
|
the original bases are saved in ``__orig_bases__`` (currently this is also
|
||||||
done by the metaclass). For example::
|
done by the metaclass). For example::
|
||||||
|
|
||||||
|
@ -152,8 +153,8 @@ done by the metaclass). For example::
|
||||||
def __init__(self, origin, item):
|
def __init__(self, origin, item):
|
||||||
self.origin = origin
|
self.origin = origin
|
||||||
self.item = item
|
self.item = item
|
||||||
def __mro_entry__(self, bases):
|
def __mro_entries__(self, bases):
|
||||||
return self.origin
|
return (self.origin,)
|
||||||
|
|
||||||
class NewList:
|
class NewList:
|
||||||
def __class_getitem__(cls, item):
|
def __class_getitem__(cls, item):
|
||||||
|
@ -180,7 +181,7 @@ Dynamic class creation and ``types.resolve_bases``
|
||||||
call ``type('Tokens', (List[int],), {})`` will fail. This is done for
|
call ``type('Tokens', (List[int],), {})`` will fail. This is done for
|
||||||
performance reasons and to minimize the number of implicit transformations.
|
performance reasons and to minimize the number of implicit transformations.
|
||||||
Instead, a helper function ``resolve_bases`` will be added to
|
Instead, a helper function ``resolve_bases`` will be added to
|
||||||
the ``types`` module to allow an explicit ``__mro_entry__`` resolution in
|
the ``types`` module to allow an explicit ``__mro_entries__`` resolution in
|
||||||
the context of dynamic class creation. Correspondingly, ``types.new_class``
|
the context of dynamic class creation. Correspondingly, ``types.new_class``
|
||||||
will be updated to reflect the new class creation steps while maintaining
|
will be updated to reflect the new class creation steps while maintaining
|
||||||
the backwards compatibility::
|
the backwards compatibility::
|
||||||
|
@ -199,21 +200,32 @@ Backwards compatibility and impact on users who don't use ``typing``
|
||||||
====================================================================
|
====================================================================
|
||||||
|
|
||||||
This proposal may break code that currently uses the names
|
This proposal may break code that currently uses the names
|
||||||
``__class_getitem__`` and ``__mro_entry__``. (But the language
|
``__class_getitem__`` and ``__mro_entries__``. (But the language
|
||||||
reference explicitly reserves *all* undocumented dunder names, and
|
reference explicitly reserves *all* undocumented dunder names, and
|
||||||
allows "breakage without warning"; see [6]_.)
|
allows "breakage without warning"; see [6]_.)
|
||||||
|
|
||||||
This proposal will support almost complete backwards compatibility with
|
This proposal will support almost complete backwards compatibility with
|
||||||
the current public generic types API; moreover the ``typing`` module is still
|
the current public generic types API; moreover the ``typing`` module is still
|
||||||
provisional. The only two exceptions are that currently
|
provisional. The only two exceptions are that currently
|
||||||
``issubclass(List[int], List)`` returns True, while with this proposal it will raise
|
``issubclass(List[int], List)`` returns True, while with this proposal it will
|
||||||
``TypeError``. Also ``issubclass(collections.abc.Iterable, typing.Iterable)``
|
raise ``TypeError``, and ``repr()`` of unsubscripted user-defined generics
|
||||||
will return ``False``, which is probably desirable, since currently we have
|
cannot be tweaked and will coincide with ``repr()`` of normal (non-generic)
|
||||||
a (virtual) inheritance cycle between these two classes.
|
classes.
|
||||||
|
|
||||||
With the reference implementation I measured negligible performance effects
|
With the reference implementation I measured negligible performance effects
|
||||||
(under 1% on a micro-benchmark) for regular (non-generic) classes.
|
(under 1% on a micro-benchmark) for regular (non-generic) classes. At the same
|
||||||
|
time performance of generics is significantly improved:
|
||||||
|
|
||||||
|
* ``importlib.reload(typing)`` is up to 7x faster
|
||||||
|
* Creation of user defined generic classes is up to 4x faster (on a micro-
|
||||||
|
benchmark with an empty body)
|
||||||
|
* Instantiation of generic classes is up to 5x faster (on a micro-benchmark
|
||||||
|
with an empty ``__init__``)
|
||||||
|
* Other operations with generic types and instances (like method lookup and
|
||||||
|
``isinstance()`` checks) are improved by around 10-20%
|
||||||
|
* The only aspect that gets slower with the current proof of concept
|
||||||
|
implementation is the subscripted generics cache look-up. However it was
|
||||||
|
already very efficient, so this aspect gives negligible overall impact.
|
||||||
|
|
||||||
References
|
References
|
||||||
==========
|
==========
|
||||||
|
@ -228,7 +240,8 @@ References
|
||||||
(https://github.com/python/typing/issues/392)
|
(https://github.com/python/typing/issues/392)
|
||||||
|
|
||||||
.. [4] The reference implementation
|
.. [4] The reference implementation
|
||||||
(https://github.com/ilevkivskyi/cpython/pull/2/files)
|
(https://github.com/ilevkivskyi/cpython/pull/2/files,
|
||||||
|
https://github.com/ilevkivskyi/cpython/tree/new-typing)
|
||||||
|
|
||||||
.. [5] Original proposal
|
.. [5] Original proposal
|
||||||
(https://github.com/python/typing/issues/468)
|
(https://github.com/python/typing/issues/468)
|
||||||
|
|
Loading…
Reference in New Issue