Redefining __call__ in an instance

Peter Otten __peter__ at web.de
Fri Jan 16 04:59:54 EST 2004


Robert Ferrell wrote:

> I have a question about assigning __call__ to an instance to make that
> instance callable.  I know there has been quite a bit of discussion
> about this, and I've read all I can find, but I'm still confused.
> 
> I'd like to have a factory class that takes a string argument and returns
> the appropriate factory method based on that string.  I'd like the
> instances to be callable.  Like this:
> 
> fact = Factory('SomeThing')
> aSomeThing = fact(some args)
> 
> anotherFact = Factory('SomeThingElse')
> anotherThing = anotherFact(some other args)

I think fact and anotherFact could be methods instead of classes, e. g.

fact = Factory("SomeThing").someMethod # see complete example below

# Here's something that should meet your specs:

class Factory:
    def __init__(self, times=1, *args):
        self.times=times
    def something(self, a1="alpha",*args):
        print "something"
        return a1*self.times
    def somethingElse(self, a1="beta", *args):
        print "something else"
        return a1*self.times

def factory(what, *initargs):
    """ factory with one instance per call """
    return getattr(Factory(*initargs), what)

f1 = factory("something")
f2 = factory("somethingElse", 2)

for f in (f1, f2):
    print "%s() --> %s" % (f.__name__, f())

> The way I thought to do this was to assign the __call__ attribute of
> the fact instance to the appropriate factory method in __init__.  That
> does not
> work, as many others have pointed out.  I know there are workarounds.
> The appended code shows the variants I know of.  I can use one of
> them, but they are not quite what I am looking for.
> 
> Have I missed the key message that explains how to make new-style
> classes callable, with the called method unique to each instance?

Why not be generous and make a dedicated (sub)class for each kind of call?
Every instance of a subclass of Callable is just a stateful function.

# Here's what I would prefer:
class Callable:
    def __init__(self, times=1, *args):
        self.times=times

class MoreCallable(Callable):
    def __call__(self, a1="gamma",*args):
        print "more"
        return a1*self.times

class OrLessCallable(Callable):
    def __call__(self, a1="delta",*args):
        print "or less"
        return a1*self.times

# a bare bones registry
_callables = {
    "more": MoreCallable,
    "less": OrLessCallable
}

def factory(what, *initargs):
    # a variant that uses Callable instances
    # instead of classes could easily be devised
    return _callables[what](*initargs)

for f in (factory("more"), factory("less", 3)):
    print "%s() --> %s" % (f.__class__.__name__, f())

Both variants respect default arguments.

Peter




More information about the Python-list mailing list