Redefining __call__ in an instance

Discussion in 'Python' started by Robert Ferrell, Jan 15, 2004.

  1. 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)

    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?

    thanks,
    -robert

    """Test use of __call__ in a (new style) class.
    The classes below show various ways of making instances
    of a class callable. The goal is to make an instance callable,
    with the called method defined distincly for each instance.
    """


    def firstFunc(word = 'up'):
    """This is the method to call, when an instance is invoked."""
    print "This is firstFunc, word %s." % word
    return

    class callWorks(object):
    """This works, since the called method is defined in the class."""
    def __init__(self):
    pass
    def __call__(self, word = 'up'):
    print 'This is inside callWorks, word %s.' % word
    return

    class callNoWork(object):
    """This doesn't work, since __call__ is defined for the method,
    not the class."""
    def __init__(self):
    # This does not make an instance of callNoWork callable
    self.__call__ = firstFunc

    class callWorksNoFun(object):
    """This works, but since the class's method is being called, the
    default arguments are defined by the class, and do not
    properly reflect the default arguments of the method that
    wants to be called."""
    def __init__(self):
    self._callFunc = firstFunc
    def __call__(self, word = None):
    # Although an instance of callWorksNoFun is callable,
    # the default arguments are wrong
    self._callFunc(word)
    return

    class addCallAttribute(object):
    """Add the attribute 'callMe', which is the callable function.
    This works fine, but requires the user to invoke this as
    instance.callMe(), rather than just instance()."""
    def __init__(self):
    self.callMe = firstFunc

    # Simplest thing
    cw = callWorks()
    cw()

    # Natural thing to try, but doesn't work
    cnw = callNoWork()
    # The instance, cnw, is not callable.
    try:
    cnw()
    except Exception, exception:
    print 'Call did not work, gave exception: %s.' % exception

    # Works, but actually invoking class method, not instance's method
    cwNF = callWorksNoFun()
    # The instance cwNF is callable, but the default value for the callable is wrong.
    # This works fine
    cwNF('No up')
    # This should default to print 'Up', but instead it defaults to None.
    cwNF()

    # Fine, but requires user to invoke instance.callMe(), rather than just instance().
    aCA = addCallAttribute()
    # To call the instance, use the callMe attribute. That respects defaults fine.
    aCA.callMe()
    Robert Ferrell, Jan 15, 2004
    #1
    1. Advertising

  2. def firstFunc (s, word='up'):
    print "foo"

    class callNoWork(object):
    def __new__ (cls):
    cls.__call__ = firstFunc
    return object.__new__(cls)

    callNoWork()()

    # Dunno if you've read this, but it explains this:
    # http://python.org/2.2.1/descrintro.html

    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)
    >
    > 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?
    >
    > thanks,
    > -robert
    >
    > """Test use of __call__ in a (new style) class.
    > The classes below show various ways of making instances
    > of a class callable. The goal is to make an instance callable,
    > with the called method defined distincly for each instance.
    > """
    >
    >
    > def firstFunc(word = 'up'):
    > """This is the method to call, when an instance is invoked."""
    > print "This is firstFunc, word %s." % word
    > return
    >
    > class callWorks(object):
    > """This works, since the called method is defined in the class."""
    > def __init__(self):
    > pass
    > def __call__(self, word = 'up'):
    > print 'This is inside callWorks, word %s.' % word
    > return
    >
    > class callNoWork(object):
    > """This doesn't work, since __call__ is defined for the method,
    > not the class."""
    > def __init__(self):
    > # This does not make an instance of callNoWork callable
    > self.__call__ = firstFunc
    >
    > class callWorksNoFun(object):
    > """This works, but since the class's method is being called, the
    > default arguments are defined by the class, and do not
    > properly reflect the default arguments of the method that
    > wants to be called."""
    > def __init__(self):
    > self._callFunc = firstFunc
    > def __call__(self, word = None):
    > # Although an instance of callWorksNoFun is callable,
    > # the default arguments are wrong
    > self._callFunc(word)
    > return
    >
    > class addCallAttribute(object):
    > """Add the attribute 'callMe', which is the callable function.
    > This works fine, but requires the user to invoke this as
    > instance.callMe(), rather than just instance()."""
    > def __init__(self):
    > self.callMe = firstFunc
    >
    > # Simplest thing
    > cw = callWorks()
    > cw()
    >
    > # Natural thing to try, but doesn't work
    > cnw = callNoWork()
    > # The instance, cnw, is not callable.
    > try:
    > cnw()
    > except Exception, exception:
    > print 'Call did not work, gave exception: %s.' % exception
    >
    > # Works, but actually invoking class method, not instance's method
    > cwNF = callWorksNoFun()
    > # The instance cwNF is callable, but the default value for the callable is wrong.
    > # This works fine
    > cwNF('No up')
    > # This should default to print 'Up', but instead it defaults to None.
    > cwNF()
    >
    > # Fine, but requires user to invoke instance.callMe(), rather than just instance().
    > aCA = addCallAttribute()
    > # To call the instance, use the callMe attribute. That respects defaults fine.
    > aCA.callMe()


    --
    (------------------------------(
    )~~~~~ Jason A. Mobarak ~~~~~~~)
    (~~ aether_at_gentoo_dot_org ~~(
    )~~~~ jmob_at_unm_dot_edu ~~~~~)
    (------------------------------(
    Jason Mobarak, Jan 16, 2004
    #2
    1. Advertising

  3. Robert Ferrell

    John Roth Guest

    "Robert Ferrell" <> wrote in message
    news:...
    > 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)
    >
    > 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?


    Basically, the system does not look in the instance for the __call__
    method for new style classes. I'm not sure why not, or where this
    is defined, but that's the way it works.

    To get a different callable for each instance, you need to do something
    like this:

    class fubar(object):
    def __init__(self, callable):
    self.myfunc = callable

    def __call__(self, *a, **b):
    self.myfunc(*a, **b)



    >
    > thanks,
    > -robert
    John Roth, Jan 16, 2004
    #3
  4. Robert Ferrell

    Peter Otten Guest

    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
    Peter Otten, Jan 16, 2004
    #4
  5. Jason Mobarak <jmob@nospam__unm.edu> wrote in message news:<>...
    > def firstFunc (s, word='up'):
    > print "foo"
    >
    > class callNoWork(object):
    > def __new__ (cls):
    > cls.__call__ = firstFunc
    > return object.__new__(cls)
    >
    > callNoWork()()
    >
    > # Dunno if you've read this, but it explains this:
    > # http://python.org/2.2.1/descrintro.html


    Thanks for the pointer. I had read this a while ago, it didn't all
    sink in, and I'd forgotten about it. That was exactly the info that I
    needed. In particular, it says that __call__ is a static method, so
    it is not possible to define that differently for each instance.

    The code above was a good reminder for me, but because __call__ is a
    static method, any new instantiation redefines __call__ for all
    existing instances.

    Thanks for the pointer,
    -robert
    Robert Ferrell, Jan 21, 2004
    #5
  6. Robert Ferrell

    John Roth Guest

    "Robert Ferrell" <> wrote in message
    news:...
    > Jason Mobarak <jmob@nospam__unm.edu> wrote in message

    news:<>...
    > > def firstFunc (s, word='up'):
    > > print "foo"
    > >
    > > class callNoWork(object):
    > > def __new__ (cls):
    > > cls.__call__ = firstFunc
    > > return object.__new__(cls)
    > >
    > > callNoWork()()
    > >
    > > # Dunno if you've read this, but it explains this:
    > > # http://python.org/2.2.1/descrintro.html

    >
    > Thanks for the pointer. I had read this a while ago, it didn't all
    > sink in, and I'd forgotten about it. That was exactly the info that I
    > needed. In particular, it says that __call__ is a static method, so
    > it is not possible to define that differently for each instance.
    >
    > The code above was a good reminder for me, but because __call__ is a
    > static method, any new instantiation redefines __call__ for all
    > existing instances.


    Huh, what? __call__() is *not* a static method. It's a perfectly
    ordinary instance method, and there is nothing in the document
    above that says anything different.

    What it is is a static *attribute.* That's a different animal
    entirely. Static attributes cannot be overridden by defining
    them in an instance.

    The entire issue is that old style and new style classes
    treat a __call__ method in an instance differently. New style
    classes ignore it, old style classes use it.

    John Roth

    >
    > Thanks for the pointer,
    > -robert
    John Roth, Jan 21, 2004
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Patrick Lioi
    Replies:
    7
    Views:
    346
    Beni Cherniavsky
    Aug 19, 2003
  2. chenyu
    Replies:
    1
    Views:
    286
  3. Robert Brewer

    RE: Redefining __call__ in an instance

    Robert Brewer, Jan 16, 2004, in forum: Python
    Replies:
    1
    Views:
    428
    Jason Mobarak
    Jan 16, 2004
  4. Ksenia Marasanova
    Replies:
    2
    Views:
    326
    Ksenia Marasanova
    Sep 9, 2005
  5. Steve Holden

    Re: instance has no __call__ method

    Steve Holden, Dec 11, 2010, in forum: Python
    Replies:
    0
    Views:
    1,211
    Steve Holden
    Dec 11, 2010
Loading...

Share This Page