R
Robert Ferrell
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()
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()