Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Monkeypatching an object to become callable

15 views
Skip to first unread message

Nikolaus Rath

unread,
Aug 9, 2009, 3:02:57 PM8/9/09
to
Hi,

I want to monkeypatch an object so that it becomes callable, although
originally it is not meant to be. (Yes, I think I do have a good reason
to do so).

But simply adding a __call__ attribute to the object apparently isn't
enough, and I do not want to touch the class object (since it would
modify all the instances):

>>> class foo(object):
... pass
...
>>> t = foo()
>>> def test():
... print 'bar'
...
>>> t()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'foo' object is not callable
>>> t.__call__ = test
>>> t()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'foo' object is not callable
>>> t.__call__()
bar


Is there an additional trick to get it to work?


Best,

-Nikolaus

--
»Time flies like an arrow, fruit flies like a Banana.«

PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

Diez B. Roggisch

unread,
Aug 9, 2009, 4:28:29 PM8/9/09
to
Nikolaus Rath schrieb:

AFAIK special methods are always only evaluated on the class. But this
works:

class Foo(object):

pass

f = Foo()

def make_callable(f):
class Callable(f.__class__):

def __call__(self):
print "foobar"

f.__class__ = Callable

make_callable(f)
f()

Diez

Carl Banks

unread,
Aug 9, 2009, 6:24:53 PM8/9/09
to
On Aug 9, 12:02 pm, Nikolaus Rath <Nikol...@rath.org> wrote:
> Hi,
>
> I want to monkeypatch an object so that it becomes callable, although
> originally it is not meant to be. (Yes, I think I do have a good reason
> to do so).
>
> But simply adding a __call__ attribute to the object apparently isn't
> enough, and I do not want to touch the class object (since it would
> modify all the instances):


Override the class's __call__, and program it to call a certain method
(say _instancecall) on the object. Catch AttributeError and raise
TypeError so that it matches the behavior when no __call__ is defined.


def __call__(self,*args,**kwargs):
try:
func = self._instancecall
except AttributeError:
raise TypeError("'%s' object not callable" % self.__class__)
return func(*args,**kwargs)


Note: don't call _instancecall inside the try clause; you don't want
to catch attribute errors raised inside the _instancecall method.

Then set _instancecall on any objects you want to be callable.


Carl Banks

7stud

unread,
Aug 9, 2009, 8:55:26 PM8/9/09
to


With an old-style class your code will work:

class A:
pass

def test():
print "test"

a = A()
a.__call__ = test

a()

--output:--
test


a2 = A()
a2()

--output:--

a2()
AttributeError: A instance has no __call__ method

Another option is to use the *decorator pattern*. The decorator
pattern can be used when you want to add additional methods and
attributes to an object, and you don't want to disturb the original
class:

class A(object):
def __init__(self, x):
self.x = x

def sayhi(self):
print "hi"


class Wrapper(object):
def __init__(self, obj, func):
self.obj = obj
self.func = func

def __call__(self, *args):
return self.func(*args)

def __getattr__(self, name):
return object.__getattribute__(self.obj, name)


def test():
print "test"

a = A(10)
w = Wrapper(a, test)
w()
print w.x
w.sayhi()

--output:--
test
10
hi

Bruno Desthuilliers

unread,
Aug 10, 2009, 4:08:32 AM8/10/09
to
7stud a �crit :
(snip)

> class Wrapper(object):
> def __init__(self, obj, func):
> self.obj = obj
> self.func = func
>
> def __call__(self, *args):
> return self.func(*args)
>
> def __getattr__(self, name):
> return object.__getattribute__(self.obj, name)

This should be

return getattr(self.obj, name)

directly calling object.__getattribute__ might skip redefinition of
__getattribute__ in self.obj.__class__ or it's mro.

Nikolaus Rath

unread,
Aug 11, 2009, 7:21:16 PM8/11/09
to
Bruno Desthuilliers <bruno.42.de...@websiteburo.invalid> writes:
> 7stud a écrit :

Works nicely, thanks. I came up with the following shorter version which
modifies the object in-place:

class Modifier(obj.__class__):
def __call__(self):
return fn()

obj.__class__ = Modifier


To me this seems a bit more elegant (less code, less underscores). Or
are there some cases where the above would fail?

Gabriel Genellina

unread,
Aug 13, 2009, 3:19:05 AM8/13/09
to pytho...@python.org
En Tue, 11 Aug 2009 20:21:16 -0300, Nikolaus Rath <Niko...@rath.org>
escribiᅵ:

> Bruno Desthuilliers <bruno.42.de...@websiteburo.invalid> writes:
>> 7stud a ᅵcrit :


>> (snip)
>>> class Wrapper(object):
>>> def __init__(self, obj, func):
>>> self.obj = obj
>>> self.func = func
>>>
>>> def __call__(self, *args):
>>> return self.func(*args)
>>>
>>> def __getattr__(self, name):
>>> return object.__getattribute__(self.obj, name)
>>
>> This should be
>>
>> return getattr(self.obj, name)
>>
>> directly calling object.__getattribute__ might skip redefinition of
>> __getattribute__ in self.obj.__class__ or it's mro.
>
> Works nicely, thanks. I came up with the following shorter version which
> modifies the object in-place:
>
> class Modifier(obj.__class__):
> def __call__(self):
> return fn()
>
> obj.__class__ = Modifier
>
>
> To me this seems a bit more elegant (less code, less underscores). Or
> are there some cases where the above would fail?

I assume the above code is inside a function like make_callable(obj, fn)
Then, a new class is created for every instance you make callable; you may
want to cache all those classes (classes aren't cheap).

Might fail: when obj is not a new-style class, or its __class__ isn't
writable (e.g. builtin types).

--
Gabriel Genellina

0 new messages