[Python-checkins] peps: Use C3-based linearization for ABC support to improve predictability
lukasz.langa
python-checkins at python.org
Mon Jul 1 14:46:25 CEST 2013
http://hg.python.org/peps/rev/000a8986ef73
changeset: 4968:000a8986ef73
user: Łukasz Langa <lukasz at langa.pl>
date: Mon Jul 01 14:46:15 2013 +0200
summary:
Use C3-based linearization for ABC support to improve predictability
files:
pep-0443.txt | 73 ++++++++++++++++++++++-----------------
1 files changed, 41 insertions(+), 32 deletions(-)
diff --git a/pep-0443.txt b/pep-0443.txt
--- a/pep-0443.txt
+++ b/pep-0443.txt
@@ -193,48 +193,37 @@
importantly, it introduces support for Abstract Base Classes (ABC).
When a generic function implementation is registered for an ABC, the
-dispatch algorithm switches to a mode of MRO calculation for the
-provided argument which includes the relevant ABCs. The algorithm is as
-follows::
+dispatch algorithm switches to an extended form of C3 linearization,
+which includes the relevant ABCs in the MRO of the provided argument.
+The algorithm inserts ABCs where their functionality is introduced, i.e.
+``issubclass(cls, abc)`` returns ``True`` for the class itself but
+returns ``False`` for all its direct base classes. Implicit ABCs for
+a given class (either registered or inferred from the presence of
+a special method like ``__len__()``) are inserted directly after the
+last ABC explicitly listed in the MRO of said class.
- def _compose_mro(cls, haystack):
- """Calculates the MRO for a given class `cls`, including relevant
- abstract base classes from `haystack`."""
- bases = set(cls.__mro__)
- mro = list(cls.__mro__)
- for regcls in haystack:
- if regcls in bases or not issubclass(cls, regcls):
- continue # either present in the __mro__ or unrelated
- for index, base in enumerate(mro):
- if not issubclass(base, regcls):
- break
- if base in bases and not issubclass(regcls, base):
- # Conflict resolution: put classes present in __mro__
- # and their subclasses first.
- index += 1
- mro.insert(index, regcls)
- return mro
-
-In its most basic form, it returns the MRO for the given type::
+In its most basic form, this linearization returns the MRO for the given
+type::
>>> _compose_mro(dict, [])
[<class 'dict'>, <class 'object'>]
-When the haystack consists of ABCs that the specified type is a subclass
-of, they are inserted in a predictable order::
+When the second argument contains ABCs that the specified type is
+a subclass of, they are inserted in a predictable order::
>>> _compose_mro(dict, [Sized, MutableMapping, str,
... Sequence, Iterable])
[<class 'dict'>, <class 'collections.abc.MutableMapping'>,
- <class 'collections.abc.Iterable'>, <class 'collections.abc.Sized'>,
+ <class 'collections.abc.Mapping'>, <class 'collections.abc.Sized'>,
+ <class 'collections.abc.Iterable'>, <class 'collections.abc.Container'>,
<class 'object'>]
While this mode of operation is significantly slower, all dispatch
decisions are cached. The cache is invalidated on registering new
implementations on the generic function or when user code calls
-``register()`` on an ABC to register a new virtual subclass. In the
-latter case, it is possible to create a situation with ambiguous
-dispatch, for instance::
+``register()`` on an ABC to implicitly subclass it. In the latter case,
+it is possible to create a situation with ambiguous dispatch, for
+instance::
>>> from collections import Iterable, Container
>>> class P:
@@ -261,20 +250,38 @@
RuntimeError: Ambiguous dispatch: <class 'collections.abc.Container'>
or <class 'collections.abc.Iterable'>
-Note that this exception would not be raised if ``Iterable`` and
-``Container`` had been provided as base classes during class definition.
-In this case dispatch happens in the MRO order::
+Note that this exception would not be raised if one or more ABCs had
+been provided explicitly as base classes during class definition. In
+this case dispatch happens in the MRO order::
>>> class Ten(Iterable, Container):
... def __iter__(self):
... for i in range(10):
... yield i
... def __contains__(self, value):
- ... return value in range(10)
+ ... return value in range(10)
...
>>> g(Ten())
'iterable'
+A similar conflict arises when subclassing an ABC is inferred from the
+presence of a special method like ``__len__()`` or ``__contains__()``::
+
+ >>> class Q:
+ ... def __contains__(self, value):
+ ... return False
+ ...
+ >>> issubclass(Q, Container)
+ True
+ >>> Iterable.register(Q)
+ >>> g(Q())
+ Traceback (most recent call last):
+ ...
+ RuntimeError: Ambiguous dispatch: <class 'collections.abc.Container'>
+ or <class 'collections.abc.Iterable'>
+
+An early version of the PEP contained a custom approach that was simpler
+but created a number of edge cases with surprising results [#why-c3]_.
Usage Patterns
==============
@@ -378,6 +385,8 @@
a particular annotation style".
(http://www.python.org/dev/peps/pep-0008)
+.. [#why-c3] http://bugs.python.org/issue18244
+
.. [#pep-3124] http://www.python.org/dev/peps/pep-3124/
.. [#peak-rules] http://peak.telecommunity.com/DevCenter/PEAK_2dRules
--
Repository URL: http://hg.python.org/peps
More information about the Python-checkins
mailing list