Callable modules?

Huaiyu Zhu huaiyu at gauss.almadan.ibm.com
Thu Jul 25 13:10:38 EDT 2002


Alex Martelli <aleax at aleax.it> wrote:
>I don't think we're really communicating.

That is probably true.  I'm thinking about usage situations where you want
to pass callable objects around as if they are functions and objects at the
same time.

>> 
>> If you take b as the object, you need to use explict call.  If you take
>
>Right!  Explicit is better than implicit.  If __call__ didn't
>exist, and by universal convention an instance's "primary method
>if any" was named call, this would be exactly as expected.
>
>> b.call as object, you can't do other things to the object.  Callable
>
>Bound methods currently are not allowed to have arbitrary
>rebindable attributes, while objects of other types can.  But
>that's just how things stand today.  Not long ago, functions
>were not allowed to have arbitrary rebindable attributes
>either -- when that was added, it made a miniscule difference
>to Python usage, possibilities, and convenience, simply because
>the use cases are SO very rare.  If rebinding attributes of
>callables on the fly was deemed important, it would be child's
>play to add that to bound methods too, of course -- might be
>even simpler than for functions, since there would be no need
>to invent a dict for the purpose.

That would be just a change of convention from __call__ to primary.

>Do you REALLY think that having b.call.x = 23 become convenient
>shorthand for b.call.im_self.x = 23 would make a DEEP difference
>to Python?  I see it as trivial enough to not be worth the
>bother -- not a frequent need, and all that.  

Well, it may be easy in some sense, but it certainly is not rare.  Here's a
typical example.  Suppose we have some functionals:

def integration(func, lower, upper): ...
def optimize(func, start): ...

Usually the actual functions have some parameters that may be changed.  With
__call__, we can have the following applications

def app(f):
    p = 1
    for i in range(20):
        p = optimize(f, p)
        f.dothings()
f = Callable()
app(f)

Without __call__, we can either use the same f, with

def app1(f):
    p = 1
    for i in range(20):
        p = optimize(f.call, p)
        f.dothings()

or use f.call, with

def app2(f):
    p = 1
    for i in range(20):
        p = optimize(f, p)
        f.im_self.dothings()

It is true that neither is significantly more difficult.  But having to keep
track of the distinction between f and f.call in applications where they act
naturally as functions is, IMHO, unnecessary burden.  Having to choose
between app1 and app2 (and for app1 having to determine the name of primary
method) before hand also makes the design brittle.

In my experience most functions fall into one of three categories:

- simple functions: sin, sqrt ...
- higher order functions: map, filter, integration
- application specific functions: callable objects with attributes
- application specific higher order functions: using callable objects

In my applications the last two categories are by far the most numerous.  So
this situation is quite common.  I still remember how much trouble I had
years ago with fortran's named common blocks for simulating parameterized
functions so that integration can work on them.  Of course that might just
be my style that relies on using many higher order functions.

Huaiyu



More information about the Python-list mailing list