[Python-checkins] peps: Resolve the open issue on ABC support, link the reference implementation.
lukasz.langa
python-checkins at python.org
Sat May 25 14:03:42 CEST 2013
http://hg.python.org/peps/rev/66ff54bad2ba
changeset: 4908:66ff54bad2ba
user: Łukasz Langa <lukasz at langa.pl>
date: Sat May 25 14:03:31 2013 +0200
summary:
Resolve the open issue on ABC support, link the reference implementation.
files:
pep-0443.txt | 90 ++++++++++++++++++++++++++++++++++++----
1 files changed, 81 insertions(+), 9 deletions(-)
diff --git a/pep-0443.txt b/pep-0443.txt
--- a/pep-0443.txt
+++ b/pep-0443.txt
@@ -8,7 +8,7 @@
Type: Standards Track
Content-Type: text/x-rst
Created: 22-May-2013
-Post-History: 22-May-2013
+Post-History: 22-May-2013, 25-May-2013
Replaces: 245, 246, 3124
@@ -147,13 +147,8 @@
The functionality described in this PEP is already implemented in the
``pkgutil`` standard library module as ``simplegeneric``. Because this
-implementation is mature, the goal is to move it largely as-is.
-
-The current implementation relies on ``__mro__`` alone, it will be made
-compatible with Abstract Base Classes' ``register()``/``unregister()``
-functionality. A possible solution has been proposed by PJE on the
-original issue for exposing ``pkgutil.simplegeneric`` as part of the
-``functools`` API [#issue-5135]_.
+implementation is mature, the goal is to move it largely as-is. The
+reference implementation is available on hg.python.org [#ref-impl]_.
The dispatch type is specified as a decorator argument. An alternative
form using function annotations has been considered but its inclusion
@@ -165,6 +160,80 @@
following the convention on registering virtual subclasses on Abstract
Base Classes, the dispatch registry will not be thread-safe.
+Abstract Base Classes
+---------------------
+
+The ``pkgutil.simplegeneric`` implementation relied on several forms of
+method resultion order (MRO). ``@singledispatch`` removes special
+handling of old-style classes and Zope's ExtensionClasses. More
+importantly, it introduces support for Abstract Base Classes (ABC).
+
+When a generic function overload 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::
+
+ 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
+
+While this mode of operation is significantly slower, no caching is
+involved because user code may ``register()`` a new class on an ABC at
+any time. In such case, it is possible to create a situation with
+ambiguous dispatch, for instance::
+
+ >>> from collections import Iterable, Container
+ >>> class P:
+ ... pass
+ >>> Iterable.register(P)
+ <class '__main__.P'>
+ >>> Container.register(P)
+ <class '__main__.P'>
+
+Faced with ambiguity, ``@singledispatch`` refuses the temptation to
+guess::
+
+ >>> @singledispatch
+ ... def g(arg):
+ ... return "base"
+ ...
+ >>> g.register(Iterable, lambda arg: "iterable")
+ <function <lambda> at 0x108b49110>
+ >>> g.register(Container, lambda arg: "container")
+ <function <lambda> at 0x108b491c8>
+ >>> g(P())
+ Traceback (most recent call last):
+ ...
+ 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::
+
+ >>> class Ten(Iterable, Container):
+ ... def __iter__(self):
+ ... for i in range(10):
+ ... yield i
+ ... def __contains__(self, value):
+ ... return value in range(10)
+ ...
+ >>> g(Ten())
+ 'iterable'
+
Usage Patterns
==============
@@ -256,7 +325,8 @@
References
==========
-.. [#issue-5135] http://bugs.python.org/issue5135
+.. [#ref-impl]
+ http://hg.python.org/features/pep-443/file/tip/Lib/functools.py#l359
.. [#pep-0008] PEP 8 states in the "Programming Recommendations"
section that "the Python standard library will not use function
@@ -279,6 +349,8 @@
.. [#pairtype]
https://bitbucket.org/pypy/pypy/raw/default/rpython/tool/pairtype.py
+.. [#issue-5135] http://bugs.python.org/issue5135
+
Copyright
=========
--
Repository URL: http://hg.python.org/peps
More information about the Python-checkins
mailing list