Updates following recent disscussions (#469)

This commit is contained in:
Ivan Levkivskyi 2017-11-14 20:54:51 +01:00 committed by Guido van Rossum
parent 170513274d
commit 2a0cc286f2
1 changed files with 32 additions and 19 deletions

View File

@ -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)