Redefining __call__ in an instance

Jason Mobarak jmob at nospam__unm.edu
Thu Jan 15 19:55:52 EST 2004


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 ~~~~~)
(------------------------------(



More information about the Python-list mailing list