[Python-3000] Adaptation vs. Generic Functions
Phillip J. Eby
pje at telecommunity.com
Fri Apr 7 08:31:15 CEST 2006
Guido wrote:
> mros = tuple(t.__mro__ for t in types)
> n = len(mros)
> candidates = [sig for sig in self.registry
> if len(sig) == n and
> all(t in mro for t, mro in zip(sig, mros))]
I believe this can be simplified to:
n = len(types)
candidates = [sig for sig in self.registry
if len(sig) == n and
all(issubclass(s,t) for s,t in zip(sig, types))]
Similarly, I believe the dominates function can be moved out to a
top-level function in similar fashion:
def dominates(dom, sub):
return all(d is not s and issubclass(d,s) for d,s in zip(dom, sub))
Using issubclass() is more accurate in this case than using __mro__,
because __mro__ considers the order of the __bases__ to be
significant, which can lead to unexpected results due to false
precision. (i.e., because it'll be guessing about cases that are
actually ambiguous)
Unfortunately, this entire approach is subject to similar scale
issues as PyProtocols/RuleDispatch. It is much slower than a few
hand-written if-then's for the common cases, and it's even slower and
too inflexible for the "zillions of methods" cases like Zope's "view"
registry (which dispatches based on some types and a string). You
could get better performance in the common cases by just creating a
dominance hierarchy among all the methods, and then writing out
Python code that executes a series of if-then tests with the dominant
cases being tested first (and some extra branches to check for
ambiguity). This code would then run at virtually the same speed as
an identical hand-written function for the most common use cases
(which tend towards having only a handful of overloads at most), and
it would also not require any extra memory allocations for temporary
lists and tuples on each invocation of the function (because all the
state is effectively tracked in the program counter as it threads its
way among the if-then tests).
To make the same approach also work for the zillions-of-methods
cases, all that would be needed is replacing some of the if-thens
with dictionary lookups to allow N-way branches instead of just
true/false tests, and some optimization to avoid re-testing
conditions or recomputing values that have already been
computed. Most of RuleDispatch's complexity stems from the fact that
I effectively implemented a mini-interpreter for executing such N-way
dispatch trees, instead of just generating bytecode. I made that
choice because there's unfortunately no "computed goto" equivalent in
today's bytecode. (Bytecode is also rather annoyingly low-level for
this kind of work.)
More information about the Python-3000
mailing list