[Python-Dev] PEP 443 - Single-dispatch generic functions

Nick Coghlan ncoghlan at gmail.com
Fri May 24 14:08:16 CEST 2013


On Fri, May 24, 2013 at 7:54 PM, Sam Partington
<sam.partington at gmail.com> wrote:
> But isn't it much much worse than names in scope, as with assigning
> names in a scope it is only your scope that is affected :
>
> from os.path import join
> def join(wibble):
>     'overloads join in this module only'
>
> any other module is unaffected, os.path.join still calls os.path.join
>
> however with this all scopes globally are affected by the last one wins rule.

Indeed, as with any modification of process global state, generic
implementation registration is something to be approached with care.
ABC registration is similar.

There's actually three kinds of registration that can happen, and only
two of them are appropriate for libraries to do implicitly, while the
last should only be explicitly triggered from main:

* registering a class your library defines with a stdlib or third
party generic function
* registering a stdlib or third party class with a generic function
your library defines
* registering a stdlib or third party class with a stdlib or third
party generic function

The first two cases? Those are just part of defining class behaviour
or function behaviour. That's entirely up to the library developer and
an entirely sensible thing for them to be doing.

That third case? It's the moral equivalent of monkey patching, and
it's the strict prerogative of application integrators.

The core assumption is that on import, you're just providing one
component of an application, and you don't know what that application
is or what it's needs are. By contrast, when you're explicitly called
from main, then you can assume that this is an explicit request from
the *integrated* application that wants you to modify the global
state.

One of the best examples of a project that gets this distinction right
is gevent - you can general import gevent without any side effects on
the process global state. However, the gevent.monkey module exposes
monkeypatching that the *application* developer can request.

You know you have a well written library if someone else could import
every single one of your modules into their application and it would
have *zero* effect on them until they call a function. This is often
the tipping point that pushes things over from being libraries to
being frameworks: the frameworks have side effects on import that mean
they don't play well with others (implicitly configuring the logging
system is a common example - the changes to the logging module's
default behaviour in 3.2 were designed to make it easier for library
developers to *stop doing that*, because it causes spurious
incompatibilities. Messing too much with the import system as a side
effect of import is also framework-like behaviour).

Making *any* changes outside your module scope as a side effect of
import can be problematic, since even if it doesn't conflict with
another library, it has a tendency to break module reloading. One of
the minor reasons that ABC registration, and the proposed single
dispatch registration, permit silent overwriting is that being too
aggressive about enforcing "once and only once" can make module
reloading even more fragile than it is already.

Cheers,
Nick.

--
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list