Make consistent use of the term "implementation" versus "overload"
This commit is contained in:
parent
7d207bc210
commit
620f079ec8
72
pep-0443.txt
72
pep-0443.txt
|
@ -19,10 +19,11 @@ This PEP proposes a new mechanism in the ``functools`` standard library
|
|||
module that provides a simple form of generic programming known as
|
||||
single-dispatch generic functions.
|
||||
|
||||
A **generic function** is composed of multiple functions sharing the
|
||||
same name. Which form should be used during a call is determined by the
|
||||
dispatch algorithm. When the implementation is chosen based on the type
|
||||
of a single argument, this is known as **single dispatch**.
|
||||
A **generic function** is composed of multiple functions implementing
|
||||
the same operation for different types. Which implementation should be
|
||||
used during a call is determined by the dispatch algorithm. When the
|
||||
implementation is chosen based on the type of a single argument, this is
|
||||
known as **single dispatch**.
|
||||
|
||||
|
||||
Rationale and Goals
|
||||
|
@ -72,8 +73,9 @@ argument, create your function accordingly::
|
|||
... print(arg)
|
||||
|
||||
To add overloaded implementations to the function, use the
|
||||
``register()`` attribute of the generic function. It takes a type
|
||||
parameter::
|
||||
``register()`` attribute of the generic function. It is a decorator,
|
||||
taking a type parameter and decorating a function implementing the
|
||||
operation for that type::
|
||||
|
||||
>>> @fun.register(int)
|
||||
... def _(arg, verbose=False):
|
||||
|
@ -110,7 +112,8 @@ each variant independently::
|
|||
>>> fun_num is fun
|
||||
False
|
||||
|
||||
When called, the generic function dispatches on the first argument::
|
||||
When called, the generic function dispatches on the type of the first
|
||||
argument::
|
||||
|
||||
>>> fun("Hello, world.")
|
||||
Hello, world.
|
||||
|
@ -129,15 +132,17 @@ When called, the generic function dispatches on the first argument::
|
|||
>>> fun(1.23)
|
||||
0.615
|
||||
|
||||
To get the implementation for a specific type, use the ``dispatch()``
|
||||
attribute::
|
||||
Where there is no registered implementation for a specific type, its
|
||||
method resolution order is used to find a more generic implementation.
|
||||
To check which implementation will the generic function choose for
|
||||
a given type, use the ``dispatch()`` attribute::
|
||||
|
||||
>>> fun.dispatch(float)
|
||||
<function fun_num at 0x104319058>
|
||||
>>> fun.dispatch(dict)
|
||||
<function fun at 0x103fe4788>
|
||||
|
||||
To access all registered overloads, use the read-only ``registry``
|
||||
To access all registered implementations, use the read-only ``registry``
|
||||
attribute::
|
||||
|
||||
>>> fun.registry.keys()
|
||||
|
@ -180,9 +185,10 @@ 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::
|
||||
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::
|
||||
|
||||
def _compose_mro(cls, haystack):
|
||||
"""Calculates the MRO for a given class `cls`, including relevant
|
||||
|
@ -218,9 +224,10 @@ of, they are inserted in a predictable order::
|
|||
|
||||
While this mode of operation is significantly slower, all dispatch
|
||||
decisions are cached. The cache is invalidated on registering new
|
||||
overloads 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::
|
||||
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::
|
||||
|
||||
>>> from collections import Iterable, Container
|
||||
>>> class P:
|
||||
|
@ -274,24 +281,27 @@ Universal overloading does not equal *arbitrary* overloading, in the
|
|||
sense that we need not expect people to randomly redefine the behavior
|
||||
of existing functions in unpredictable ways. To the contrary, generic
|
||||
function usage in actual programs tends to follow very predictable
|
||||
patterns and overloads are highly-discoverable in the common case.
|
||||
patterns and registered implementations are highly-discoverable in the
|
||||
common case.
|
||||
|
||||
If a module is defining a new generic operation, it will usually also
|
||||
define any required overloads for existing types in the same place.
|
||||
Likewise, if a module is defining a new type, then it will usually
|
||||
define overloads there for any generic functions that it knows or cares
|
||||
about. As a result, the vast majority of overloads can be found adjacent
|
||||
to either the function being overloaded, or to a newly-defined type for
|
||||
which the overload is adding support.
|
||||
define any required implementations for existing types in the same
|
||||
place. Likewise, if a module is defining a new type, then it will
|
||||
usually define implementations there for any generic functions that it
|
||||
knows or cares about. As a result, the vast majority of registered
|
||||
implementations can be found adjacent to either the function being
|
||||
overloaded, or to a newly-defined type for which the implementation is
|
||||
adding support.
|
||||
|
||||
It is only in rather infrequent cases that one will have overloads in
|
||||
a module that contains neither the function nor the type(s) for which
|
||||
the overload is added. In the absence of incompetence or deliberate
|
||||
intention to be obscure, the few overloads that are not adjacent to the
|
||||
relevant type(s) or function(s), will generally not need to be
|
||||
understood or known about outside the scope where those overloads are
|
||||
defined. (Except in the "support modules" case, where best practice
|
||||
suggests naming them accordingly.)
|
||||
It is only in rather infrequent cases that one will have implementations
|
||||
registered in a module that contains neither the function nor the
|
||||
type(s) for which the implementation is added. In the absence of
|
||||
incompetence or deliberate intention to be obscure, the few
|
||||
implementations that are not registered adjacent to the relevant type(s)
|
||||
or function(s), will generally not need to be understood or known about
|
||||
outside the scope where those implementations are defined. (Except in
|
||||
the "support modules" case, where best practice suggests naming them
|
||||
accordingly.)
|
||||
|
||||
As mentioned earlier, single-dispatch generics are already prolific
|
||||
throughout the standard library. A clean, standard way of doing them
|
||||
|
|
Loading…
Reference in New Issue