__rcall__???

David C. Ullrich ullrich at math.okstate.edu
Sat Dec 18 13:23:35 EST 1999


    So why isn't there a magic __rcall__, that would go with
__call__ like the other __rop__'s go with __op__?

    It seems useful to me to allow x to alter the way f behaves
when you call f(x); in some situations it seems much cleaner
(cleaner than insisting that f have advance knowledge of all
the possible sorts of x's that could ever be passed to it, and
requiring that the source code for f be modified when a new
type of argument is added.) A few examples where it's useful
are below.

    My Function and Data objects do have __rcall__ methods,
inserted by hand - a builtin one would work better. (Also then
I wouldn't feel so evil spelling it "__rcall__"...)

DU

    For example: An instance of the Function class
encapsulates a function. The point to the whole
project is a Python math gizmo/thingie, where the
_value_ of an expression defining a function actually
_is_ the function, or rather the Function. So you can
define functions in standard mathematical notation,
plot them, etc, all without ever actually parsing the
function definition explicitly.

    Function has a subclass Composition that encapsulates
the composition of two functions. Function has a
__call__ method roughly like so:

def __call__(self, x):
    if callable(x):
        return Composition(self, x)
    else:
        return self._eval(x)

So that Sin(pi) is just 0.0, while Sin(Sin) is another function,
is the point. That's the way it started - then I found myself adding
many more special cases to Function.__call__. For example
Curve is a Data object - if F is a Function (from the plane to the
plane, say) and C is a Curve then F(C) is supposed to be another
Curve. And Function has a subclass Const; if c is a Const then
we want F(c) to be another Const, not a Composition.

    And so on - I found Function.__call__ getting more
complicated with each new feature. Then I revised it to

def __call__(self, x):
    try:
        let_x_do_it = x.query_rcall()
    except:
        let_x_do_it = 0

    print 'Sorry, Tim...'

    if let_x_do_it:
        return x.__rcall__(self)
    else:
        return self.do_call(x)

def do_call(self, x):
    if callable(x):
        return Composition(self, x)
    else:
        return self._eval(x)

I hope nobody thinks that's supposed to be designed for
maximum performance. The point is that that version
of Function.__call__ has remained unchanged through
several versions of the project; I get Functions to do the
right thing when applied to Curves and Consts just by
giving those classes appropriate query_rcall and __rcall__
methods. Other sorts of arguments are coming soon...

(Yes, it all gets very slow. Doesn't matter much, all
this stuff only gets used when the user wants to define
a new Function - that only happens once in a while.
Loops where a Function gets evaluated millions
of times happen elsewhere, in compiled code.)




More information about the Python-list mailing list