__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