summaryrefslogtreecommitdiff
path: root/src/tests/documentation.py
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2015-07-23 07:03:48 +0200
committerMichele Simionato <michele.simionato@gmail.com>2015-07-23 07:03:48 +0200
commit80072c50a765547736c03810cfb3e4f5afcb928d (patch)
tree72e254b8b8b8f542286c4f30d7dd121f9512eca9 /src/tests/documentation.py
parent14b603738eed475281a38350bfaf0df5bca5f3c8 (diff)
downloadpython-decorator-git-80072c50a765547736c03810cfb3e4f5afcb928d.tar.gz
Changed the implementation of generic functions
Diffstat (limited to 'src/tests/documentation.py')
-rw-r--r--src/tests/documentation.py80
1 files changed, 28 insertions, 52 deletions
diff --git a/src/tests/documentation.py b/src/tests/documentation.py
index f4aa995..6ce800b 100644
--- a/src/tests/documentation.py
+++ b/src/tests/documentation.py
@@ -782,6 +782,11 @@ then ``get_length`` must be defined on ``WithLength`` instances:
>>> get_length(WithLength())
0
+You can find the virtual ancestors of a given set of classes as follows:
+
+ >> get_length.vancestors(WithLength,)
+ [[<class 'collections.abc.Sized'>]]
+
Of course this is a contrived example since you could just use the
builtin ``len``, but you should get the idea.
@@ -809,69 +814,43 @@ as a virtual ancestor):
Now, let us define an implementation of ``get_length`` specific to set:
-.. code-block:: python
-
- >>> @get_length.register(collections.Set)
- ... def get_length_set(obj):
- ... return 1
+$$get_length_set
-The current implementation first check in the MRO and then look
-for virtual ancestors; since ``SomeSet`` inherits directly
-from ``collections.Sized`` that implementation is found first:
+The current implementation, as the one used by ``functools.singledispatch``,
+is able to discern that a ``Set`` is a ``Sized`` object, so the
+implementation for ``Set`` is taken:
.. code-block:: python
>>> get_length(SomeSet())
- 0
-
-Generic functions implemented via ``functools.singledispatch`` use
-a more sophisticated lookup algorithm; in particular they are able
-to discern that a ``Set`` is a ``Sized`` object, so the
-implementation for ``Set`` is taken and the result is 1, not 0.
-Still, the implementation in the decorator module is easy to
-undestand, once one declare that real ancestors take the precedence
-over virtual ancestors and the problem can be solved anyway by
-subclassing. As a matter of fact, if we define a subclass
-
-$$SomeSet2
-
-which inherits from ``collections.Set``, we get as expected
-
-.. code-block:: python
-
- >>> get_length(SomeSet2())
- 1
-
-consistently with the method resolution order, with ``Set`` having the
-precedence with respect to ``Sized``:
-
-.. code-block:: python
-
- >>> [c.__name__ for c in SomeSet2.mro()]
- ['SomeSet2', 'SomeSet', 'Set', 'Sized', 'Iterable', 'Container', 'object']
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot create a consistent method resolution
+ order (MRO) for bases Sized, Set
-The functions implemented via ``functools.singledispatch``
-are smarter when there are conflicting implementations and are
-able to solve more potential conflicts. Just to have an idea
-of what I am talking about, here is a situation with a conflict:
+Sometimes it is impossible to find the right implementation. Here is a
+situation with a type conflict. First of all, let us register
.. code-block:: python
- >>> _ = collections.Iterable.register(WithLength)
>>> @get_length.register(collections.Iterable)
... def get_length_iterable(obj):
... raise TypeError('Cannot get the length of an iterable')
- >>> get_length(WithLength())
- Traceback (most recent call last):
- ...
- RuntimeError: Ambiguous dispatch for WithLength instance: Sized or Iterable?
-Since ``WithLength`` is both a (virtual) subclass
+
+Since ``SomeSet`` is now both a (virtual) subclass
of ``collections.Iterable`` and of ``collections.Sized``, which are
not related by subclassing, it is impossible
to decide which implementation should be taken. Consistently with
the *refuse the temptation to guess* philosophy, an error is raised.
-``functools.singledispatch`` would work exactly the same in this case.
+
+ >>> get_length(SomeSet())
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot create a consistent method resolution
+ order (MRO) for bases Iterable, Sized, Set
+
+``functools.singledispatch`` would raise a similar error in this case.
Finally let me notice that the decorator module implementation does
not use any cache, whereas the one in ``singledispatch`` has a cache.
@@ -1474,9 +1453,6 @@ def get_length_sized(obj):
return len(obj)
-class SomeSet2(SomeSet, collections.Set):
- def __contains__(self, a):
- return True
-
- def __iter__(self):
- yield 1
+@get_length.register(collections.Set)
+def get_length_set(obj):
+ return 1