diff options
| author | Michele Simionato <michele.simionato@gmail.com> | 2015-07-23 07:03:48 +0200 |
|---|---|---|
| committer | Michele Simionato <michele.simionato@gmail.com> | 2015-07-23 07:03:48 +0200 |
| commit | 80072c50a765547736c03810cfb3e4f5afcb928d (patch) | |
| tree | 72e254b8b8b8f542286c4f30d7dd121f9512eca9 /src/tests/documentation.py | |
| parent | 14b603738eed475281a38350bfaf0df5bca5f3c8 (diff) | |
| download | python-decorator-git-80072c50a765547736c03810cfb3e4f5afcb928d.tar.gz | |
Changed the implementation of generic functions
Diffstat (limited to 'src/tests/documentation.py')
| -rw-r--r-- | src/tests/documentation.py | 80 |
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 |
