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``
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
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.
@ -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.
``__mro_entry__``
-----------------
``__mro_entries__``
-------------------
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,
it is called with the original tuple of bases as an argument. If the result
of the call is not ``None``, then it is substituted instead of this object.
Otherwise (if the result is ``None``), the base is just removed. This is
necessary to avoid inconsistent MRO errors, that are currently prevented by
manipulations in ``GenericMeta.__new__``. After creating the class,
definition, then ``__mro_entries__`` is searched on it. If found,
it is called with the original tuple of bases as an argument. The result
of the call must be a tuple, that is unpacked in the bases classes in place
of this object. (If the tuple is empty, this means that the original bases is
simply discarded.) Using the method API instead of just an attribute is
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
done by the metaclass). For example::
@ -152,8 +153,8 @@ done by the metaclass). For example::
def __init__(self, origin, item):
self.origin = origin
self.item = item
def __mro_entry__(self, bases):
return self.origin
def __mro_entries__(self, bases):
return (self.origin,)
class NewList:
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
performance reasons and to minimize the number of implicit transformations.
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``
will be updated to reflect the new class creation steps while maintaining
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
``__class_getitem__`` and ``__mro_entry__``. (But the language
``__class_getitem__`` and ``__mro_entries__``. (But the language
reference explicitly reserves *all* undocumented dunder names, and
allows "breakage without warning"; see [6]_.)
This proposal will support almost complete backwards compatibility with
the current public generic types API; moreover the ``typing`` module is still
provisional. The only two exceptions are that currently
``issubclass(List[int], List)`` returns True, while with this proposal it will raise
``TypeError``. Also ``issubclass(collections.abc.Iterable, typing.Iterable)``
will return ``False``, which is probably desirable, since currently we have
a (virtual) inheritance cycle between these two classes.
``issubclass(List[int], List)`` returns True, while with this proposal it will
raise ``TypeError``, and ``repr()`` of unsubscripted user-defined generics
cannot be tweaked and will coincide with ``repr()`` of normal (non-generic)
classes.
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
==========
@ -228,7 +240,8 @@ References
(https://github.com/python/typing/issues/392)
.. [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
(https://github.com/python/typing/issues/468)