Re: Overloading function calls

Tim Peters (tim@ksr.com)
Tue, 07 Jun 94 04:14:19 -0400

Again WRT coercions & RHS dispatch, I think there's one other Python-like
scheme worth considering.

The scheme I gave last time made RHS dispatch more painful to spell, on
the grounds that both coercions and RHS dispatch are unusual things for a
class to want. But assuming that's true, the scheme forces the
interpreter to do two failing attribute lookups (for x.__coerce__ and
y.__rcoerce__) on most binop invocations.

An alternative is for

x binop y

where at least one of {x,y} is an instance, to do just this:

if hasattr(x, '__binop__'):
# the usual case can't possibly be cheaper than this!
return x.__binop__(y)

# the usual case doesn't get here except to report an
# AttributeError, so it's "free"
if hasattr(y, '__binop__') and \
hasattr(y, '__rhs__'):
return y.__binop__(x, 1)

raise AttributeError, __binop__

Coercions aren't catered to at all. Classes that want RHS dispatch need
both to set the new magic data attribute __rhs__, and stick a third
"swapped=0" argument on their special binop methods; the "__rhs__"
business is needed so that classes that don't want to support RHS
dispatch (presumably most classes out there today) don't get passed a
"swapped" flag they don't expect to deal with (i.e., they can define
their binop methods with two arguments, as they do today).

In return for speeding up normal-case special binops (indeed, making them
faster than they are today), and making the implementation clearer, this
would break all existing code that relies on auto-coercion _or_ RHS
dispatch, and make full-blown numeric types substantially harder to code.

Surprising example of the last claim: faced with

Complex / Date

and assuming neither type knows how to deal with the other and both
define __div__, how does the programmer detect the error? Under the
original scheme, Complex.__coerce__(Date) and Date.__rcoerce__(Complex)
both tell Python they don't know what to do, and Python raises a
TypeError.

Under the alternative scheme, Complex.__div__(Date) is invoked, figures
out it doesn't know what to do, invokes Date.__div__(Complex,1), which
figures out it doesn't know what to do either, and ...? Probably
reinvokes Complex.__div__(Date)! I.e., Date doesn't know Complex already
tried it, and the first three cheap schemes you think of for _letting_ it
know have subtle fatal flaws <wink>.

While I prefer the original scheme, I don't think the case for it is
overwhelming -- the simpler-&-feebler-&-less-compatible but presumably
faster-for-most-people-most-of-the-time alternative may be more
attractive to many or most. Is it?

anyone-suggests-a-command-line-switch-&-i'll-personally-excommunicate-
'em-ly y'rs - tim

Tim Peters tim@ksr.com
not speaking for Kendall Square Research Corp