2013-05-22 18:27:54 -04:00
|
|
|
|
PEP: 443
|
|
|
|
|
Title: Single-dispatch generic functions
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
2018-01-27 16:19:45 -05:00
|
|
|
|
Author: Łukasz Langa <lukasz@python.org>
|
2013-05-22 18:27:54 -04:00
|
|
|
|
Discussions-To: Python-Dev <python-dev@python.org>
|
2013-06-15 10:52:08 -04:00
|
|
|
|
Status: Final
|
2013-05-22 18:27:54 -04:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 22-May-2013
|
2013-05-31 05:34:10 -04:00
|
|
|
|
Post-History: 22-May-2013, 25-May-2013, 31-May-2013
|
2013-05-22 18:27:54 -04:00
|
|
|
|
Replaces: 245, 246, 3124
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-05-27 08:23:30 -04:00
|
|
|
|
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**.
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale and Goals
|
|
|
|
|
===================
|
|
|
|
|
|
|
|
|
|
Python has always provided a variety of built-in and standard-library
|
|
|
|
|
generic functions, such as ``len()``, ``iter()``, ``pprint.pprint()``,
|
|
|
|
|
``copy.copy()``, and most of the functions in the ``operator`` module.
|
|
|
|
|
However, it currently:
|
|
|
|
|
|
|
|
|
|
1. does not have a simple or straightforward way for developers to
|
|
|
|
|
create new generic functions,
|
|
|
|
|
|
|
|
|
|
2. does not have a standard way for methods to be added to existing
|
|
|
|
|
generic functions (i.e., some are added using registration
|
|
|
|
|
functions, others require defining ``__special__`` methods, possibly
|
|
|
|
|
by monkeypatching).
|
|
|
|
|
|
|
|
|
|
In addition, it is currently a common anti-pattern for Python code to
|
|
|
|
|
inspect the types of received arguments, in order to decide what to do
|
2013-06-04 21:29:49 -04:00
|
|
|
|
with the objects.
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
2013-06-04 21:29:49 -04:00
|
|
|
|
For example, code may wish to accept either an object
|
|
|
|
|
of some type, or a sequence of objects of that type.
|
2013-05-22 18:27:54 -04:00
|
|
|
|
Currently, the "obvious way" to do this is by type inspection, but this
|
2013-06-04 21:29:49 -04:00
|
|
|
|
is brittle and closed to extension.
|
|
|
|
|
|
|
|
|
|
Abstract Base Classes make it easier
|
2013-05-22 18:27:54 -04:00
|
|
|
|
to discover present behaviour, but don't help adding new behaviour.
|
|
|
|
|
A developer using an already-written library may be unable to change how
|
|
|
|
|
their objects are treated by such code, especially if the objects they
|
|
|
|
|
are using were created by a third party.
|
|
|
|
|
|
|
|
|
|
Therefore, this PEP proposes a uniform API to address dynamic
|
|
|
|
|
overloading using decorators.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
User API
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
To define a generic function, decorate it with the ``@singledispatch``
|
|
|
|
|
decorator. Note that the dispatch happens on the type of the first
|
2013-06-04 21:29:49 -04:00
|
|
|
|
argument. Create your function accordingly::
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
>>> from functools import singledispatch
|
|
|
|
|
>>> @singledispatch
|
|
|
|
|
... def fun(arg, verbose=False):
|
|
|
|
|
... if verbose:
|
|
|
|
|
... print("Let me just say,", end=" ")
|
|
|
|
|
... print(arg)
|
|
|
|
|
|
|
|
|
|
To add overloaded implementations to the function, use the
|
2013-06-04 21:29:49 -04:00
|
|
|
|
``register()`` attribute of the generic function. This is a decorator,
|
2013-05-27 08:23:30 -04:00
|
|
|
|
taking a type parameter and decorating a function implementing the
|
|
|
|
|
operation for that type::
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
>>> @fun.register(int)
|
|
|
|
|
... def _(arg, verbose=False):
|
|
|
|
|
... if verbose:
|
|
|
|
|
... print("Strength in numbers, eh?", end=" ")
|
|
|
|
|
... print(arg)
|
|
|
|
|
...
|
|
|
|
|
>>> @fun.register(list)
|
|
|
|
|
... def _(arg, verbose=False):
|
|
|
|
|
... if verbose:
|
|
|
|
|
... print("Enumerate this:")
|
|
|
|
|
... for i, elem in enumerate(arg):
|
|
|
|
|
... print(i, elem)
|
|
|
|
|
|
|
|
|
|
To enable registering lambdas and pre-existing functions, the
|
2013-05-22 18:38:27 -04:00
|
|
|
|
``register()`` attribute can be used in a functional form::
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
>>> def nothing(arg, verbose=False):
|
|
|
|
|
... print("Nothing.")
|
|
|
|
|
...
|
|
|
|
|
>>> fun.register(type(None), nothing)
|
|
|
|
|
|
2013-06-04 21:29:49 -04:00
|
|
|
|
The ``register()`` attribute returns the undecorated function. This
|
2013-05-24 10:12:04 -04:00
|
|
|
|
enables decorator stacking, pickling, as well as creating unit tests for
|
|
|
|
|
each variant independently::
|
2013-05-23 16:15:26 -04:00
|
|
|
|
|
|
|
|
|
>>> @fun.register(float)
|
|
|
|
|
... @fun.register(Decimal)
|
|
|
|
|
... def fun_num(arg, verbose=False):
|
|
|
|
|
... if verbose:
|
|
|
|
|
... print("Half of your number:", end=" ")
|
|
|
|
|
... print(arg / 2)
|
|
|
|
|
...
|
|
|
|
|
>>> fun_num is fun
|
|
|
|
|
False
|
|
|
|
|
|
2013-05-27 08:23:30 -04:00
|
|
|
|
When called, the generic function dispatches on the type of the first
|
|
|
|
|
argument::
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
>>> fun("Hello, world.")
|
|
|
|
|
Hello, world.
|
|
|
|
|
>>> fun("test.", verbose=True)
|
|
|
|
|
Let me just say, test.
|
|
|
|
|
>>> fun(42, verbose=True)
|
|
|
|
|
Strength in numbers, eh? 42
|
|
|
|
|
>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
|
|
|
|
|
Enumerate this:
|
|
|
|
|
0 spam
|
|
|
|
|
1 spam
|
|
|
|
|
2 eggs
|
|
|
|
|
3 spam
|
|
|
|
|
>>> fun(None)
|
|
|
|
|
Nothing.
|
2013-05-24 10:12:04 -04:00
|
|
|
|
>>> fun(1.23)
|
2013-05-23 16:15:26 -04:00
|
|
|
|
0.615
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
2013-05-27 08:23:30 -04:00
|
|
|
|
Where there is no registered implementation for a specific type, its
|
|
|
|
|
method resolution order is used to find a more generic implementation.
|
2013-05-31 06:31:11 -04:00
|
|
|
|
The original function decorated with ``@singledispatch`` is registered
|
|
|
|
|
for the base ``object`` type, which means it is used if no better
|
|
|
|
|
implementation is found.
|
|
|
|
|
|
2013-05-27 08:23:30 -04:00
|
|
|
|
To check which implementation will the generic function choose for
|
|
|
|
|
a given type, use the ``dispatch()`` attribute::
|
2013-05-24 10:12:04 -04:00
|
|
|
|
|
|
|
|
|
>>> fun.dispatch(float)
|
|
|
|
|
<function fun_num at 0x104319058>
|
2013-05-31 06:31:11 -04:00
|
|
|
|
>>> fun.dispatch(dict) # note: default implementation
|
|
|
|
|
<function fun at 0x103fe0000>
|
2013-05-24 10:12:04 -04:00
|
|
|
|
|
2013-05-27 08:23:30 -04:00
|
|
|
|
To access all registered implementations, use the read-only ``registry``
|
2013-05-25 14:05:45 -04:00
|
|
|
|
attribute::
|
|
|
|
|
|
|
|
|
|
>>> fun.registry.keys()
|
|
|
|
|
dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
|
|
|
|
|
<class 'decimal.Decimal'>, <class 'list'>,
|
|
|
|
|
<class 'float'>])
|
|
|
|
|
>>> fun.registry[float]
|
|
|
|
|
<function fun_num at 0x1035a2840>
|
|
|
|
|
>>> fun.registry[object]
|
2013-05-31 06:31:11 -04:00
|
|
|
|
<function fun at 0x103fe0000>
|
2013-05-25 14:05:45 -04:00
|
|
|
|
|
2013-05-22 18:27:54 -04:00
|
|
|
|
The proposed API is intentionally limited and opinionated, as to ensure
|
|
|
|
|
it is easy to explain and use, as well as to maintain consistency with
|
|
|
|
|
existing members in the ``functools`` module.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Implementation Notes
|
|
|
|
|
====================
|
|
|
|
|
|
|
|
|
|
The functionality described in this PEP is already implemented in the
|
|
|
|
|
``pkgutil`` standard library module as ``simplegeneric``. Because this
|
2013-05-25 08:03:31 -04:00
|
|
|
|
implementation is mature, the goal is to move it largely as-is. The
|
|
|
|
|
reference implementation is available on hg.python.org [#ref-impl]_.
|
2013-05-23 16:15:26 -04:00
|
|
|
|
|
|
|
|
|
The dispatch type is specified as a decorator argument. An alternative
|
2013-06-04 21:29:49 -04:00
|
|
|
|
form using function annotations was considered but its inclusion
|
|
|
|
|
has been rejected. As of May 2013, this usage pattern is out of scope
|
|
|
|
|
for the standard library [#pep-0008]_, and the best practices for
|
2013-05-23 16:15:26 -04:00
|
|
|
|
annotation usage are still debated.
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
2013-06-04 21:29:49 -04:00
|
|
|
|
Based on the current ``pkgutil.simplegeneric`` implementation, and
|
2013-05-22 18:27:54 -04:00
|
|
|
|
following the convention on registering virtual subclasses on Abstract
|
|
|
|
|
Base Classes, the dispatch registry will not be thread-safe.
|
|
|
|
|
|
2013-05-25 08:03:31 -04:00
|
|
|
|
Abstract Base Classes
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
The ``pkgutil.simplegeneric`` implementation relied on several forms of
|
2017-12-20 11:02:36 -05:00
|
|
|
|
method resolution order (MRO). ``@singledispatch`` removes special
|
2013-05-25 08:03:31 -04:00
|
|
|
|
handling of old-style classes and Zope's ExtensionClasses. More
|
|
|
|
|
importantly, it introduces support for Abstract Base Classes (ABC).
|
|
|
|
|
|
2013-05-27 08:23:30 -04:00
|
|
|
|
When a generic function implementation is registered for an ABC, the
|
2013-07-01 08:46:15 -04:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
In its most basic form, this linearization returns the MRO for the given
|
|
|
|
|
type::
|
2013-05-25 10:12:02 -04:00
|
|
|
|
|
2013-05-25 10:21:03 -04:00
|
|
|
|
>>> _compose_mro(dict, [])
|
|
|
|
|
[<class 'dict'>, <class 'object'>]
|
2013-05-25 10:12:02 -04:00
|
|
|
|
|
2013-07-01 08:46:15 -04:00
|
|
|
|
When the second argument contains ABCs that the specified type is
|
|
|
|
|
a subclass of, they are inserted in a predictable order::
|
2013-05-25 10:12:02 -04:00
|
|
|
|
|
2013-05-25 10:21:03 -04:00
|
|
|
|
>>> _compose_mro(dict, [Sized, MutableMapping, str,
|
|
|
|
|
... Sequence, Iterable])
|
|
|
|
|
[<class 'dict'>, <class 'collections.abc.MutableMapping'>,
|
2013-07-01 08:46:15 -04:00
|
|
|
|
<class 'collections.abc.Mapping'>, <class 'collections.abc.Sized'>,
|
|
|
|
|
<class 'collections.abc.Iterable'>, <class 'collections.abc.Container'>,
|
2013-05-25 10:21:03 -04:00
|
|
|
|
<class 'object'>]
|
2013-05-25 10:12:02 -04:00
|
|
|
|
|
2013-05-25 16:06:02 -04:00
|
|
|
|
While this mode of operation is significantly slower, all dispatch
|
|
|
|
|
decisions are cached. The cache is invalidated on registering new
|
2013-05-27 08:23:30 -04:00
|
|
|
|
implementations on the generic function or when user code calls
|
2013-07-01 08:46:15 -04:00
|
|
|
|
``register()`` on an ABC to implicitly subclass it. In the latter case,
|
|
|
|
|
it is possible to create a situation with ambiguous dispatch, for
|
|
|
|
|
instance::
|
2013-05-25 08:03:31 -04:00
|
|
|
|
|
|
|
|
|
>>> 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'>
|
|
|
|
|
|
2013-07-01 08:46:15 -04:00
|
|
|
|
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::
|
2013-05-25 08:03:31 -04:00
|
|
|
|
|
|
|
|
|
>>> class Ten(Iterable, Container):
|
|
|
|
|
... def __iter__(self):
|
|
|
|
|
... for i in range(10):
|
|
|
|
|
... yield i
|
|
|
|
|
... def __contains__(self, value):
|
2013-07-01 08:46:15 -04:00
|
|
|
|
... return value in range(10)
|
2013-05-25 08:03:31 -04:00
|
|
|
|
...
|
|
|
|
|
>>> g(Ten())
|
|
|
|
|
'iterable'
|
|
|
|
|
|
2013-07-01 08:46:15 -04:00
|
|
|
|
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]_.
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
Usage Patterns
|
|
|
|
|
==============
|
|
|
|
|
|
|
|
|
|
This PEP proposes extending behaviour only of functions specifically
|
|
|
|
|
marked as generic. Just as a base class method may be overridden by
|
2013-06-04 21:29:49 -04:00
|
|
|
|
a subclass, so too a function may be overloaded to provide custom
|
2013-05-22 18:27:54 -04:00
|
|
|
|
functionality for a given type.
|
|
|
|
|
|
|
|
|
|
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
|
2013-05-27 08:23:30 -04:00
|
|
|
|
patterns and registered implementations are highly-discoverable in the
|
|
|
|
|
common case.
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
If a module is defining a new generic operation, it will usually also
|
2013-05-27 08:23:30 -04:00
|
|
|
|
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 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.)
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
As mentioned earlier, single-dispatch generics are already prolific
|
|
|
|
|
throughout the standard library. A clean, standard way of doing them
|
|
|
|
|
provides a way forward to refactor those custom implementations to use
|
|
|
|
|
a common one, opening them up for user extensibility at the same time.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Alternative approaches
|
|
|
|
|
======================
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
In :pep:`3124` Phillip J. Eby proposes a full-grown solution
|
2013-05-22 18:27:54 -04:00
|
|
|
|
with overloading based on arbitrary rule sets (with the default
|
|
|
|
|
implementation dispatching on argument types), as well as interfaces,
|
|
|
|
|
adaptation and method combining. PEAK-Rules [#peak-rules]_ is
|
|
|
|
|
a reference implementation of the concepts described in PJE's PEP.
|
|
|
|
|
|
|
|
|
|
Such a broad approach is inherently complex, which makes reaching
|
|
|
|
|
a consensus hard. In contrast, this PEP focuses on a single piece of
|
|
|
|
|
functionality that is simple to reason about. It's important to note
|
|
|
|
|
this does not preclude the use of other approaches now or in the future.
|
|
|
|
|
|
|
|
|
|
In a 2005 article on Artima [#artima2005]_ Guido van Rossum presents
|
|
|
|
|
a generic function implementation that dispatches on types of all
|
|
|
|
|
arguments on a function. The same approach was chosen in Andrey Popp's
|
|
|
|
|
``generic`` package available on PyPI [#pypi-generic]_, as well as David
|
|
|
|
|
Mertz's ``gnosis.magic.multimethods`` [#gnosis-multimethods]_.
|
|
|
|
|
|
|
|
|
|
While this seems desirable at first, I agree with Fredrik Lundh's
|
|
|
|
|
comment that "if you design APIs with pages of logic just to sort out
|
|
|
|
|
what code a function should execute, you should probably hand over the
|
|
|
|
|
API design to someone else". In other words, the single argument
|
|
|
|
|
approach proposed in this PEP is not only easier to implement but also
|
|
|
|
|
clearly communicates that dispatching on a more complex state is an
|
|
|
|
|
anti-pattern. It also has the virtue of corresponding directly with the
|
|
|
|
|
familiar method dispatch mechanism in object oriented programming. The
|
|
|
|
|
only difference is whether the custom implementation is associated more
|
|
|
|
|
closely with the data (object-oriented methods) or the algorithm
|
|
|
|
|
(single-dispatch overloading).
|
|
|
|
|
|
2013-05-23 10:08:26 -04:00
|
|
|
|
PyPy's RPython offers ``extendabletype`` [#pairtype]_, a metaclass which
|
|
|
|
|
enables classes to be externally extended. In combination with
|
|
|
|
|
``pairtype()`` and ``pair()`` factories, this offers a form of
|
|
|
|
|
single-dispatch generics.
|
|
|
|
|
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
Acknowledgements
|
|
|
|
|
================
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
Apart from Phillip J. Eby's work on :pep:`3124` and
|
2013-05-22 18:27:54 -04:00
|
|
|
|
PEAK-Rules, influences include Paul Moore's original issue
|
|
|
|
|
[#issue-5135]_ that proposed exposing ``pkgutil.simplegeneric`` as part
|
|
|
|
|
of the ``functools`` API, Guido van Rossum's article on multimethods
|
|
|
|
|
[#artima2005]_, and discussions with Raymond Hettinger on a general
|
2013-05-23 10:08:26 -04:00
|
|
|
|
pprint rewrite. Huge thanks to Nick Coghlan for encouraging me to create
|
|
|
|
|
this PEP and providing initial feedback.
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
2013-05-25 08:03:31 -04:00
|
|
|
|
.. [#ref-impl]
|
|
|
|
|
http://hg.python.org/features/pep-443/file/tip/Lib/functools.py#l359
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
.. [#pep-0008] :pep:`8` states in the "Programming Recommendations"
|
2013-05-22 18:27:54 -04:00
|
|
|
|
section that "the Python standard library will not use function
|
|
|
|
|
annotations as that would result in a premature commitment to
|
|
|
|
|
a particular annotation style".
|
|
|
|
|
|
2013-07-01 08:46:15 -04:00
|
|
|
|
.. [#why-c3] http://bugs.python.org/issue18244
|
|
|
|
|
|
2013-05-22 18:27:54 -04:00
|
|
|
|
.. [#peak-rules] http://peak.telecommunity.com/DevCenter/PEAK_2dRules
|
|
|
|
|
|
|
|
|
|
.. [#artima2005]
|
|
|
|
|
http://www.artima.com/weblogs/viewpost.jsp?thread=101605
|
|
|
|
|
|
|
|
|
|
.. [#pypi-generic] http://pypi.python.org/pypi/generic
|
|
|
|
|
|
|
|
|
|
.. [#gnosis-multimethods]
|
|
|
|
|
http://gnosis.cx/publish/programming/charming_python_b12.html
|
|
|
|
|
|
2013-05-23 10:08:26 -04:00
|
|
|
|
.. [#pairtype]
|
|
|
|
|
https://bitbucket.org/pypy/pypy/raw/default/rpython/tool/pairtype.py
|
|
|
|
|
|
2013-05-25 08:03:31 -04:00
|
|
|
|
.. [#issue-5135] http://bugs.python.org/issue5135
|
|
|
|
|
|
2013-05-22 18:27:54 -04:00
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|