Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why won't dynamically adding a `__call__` method to an instance work?

In both Python 2 and Python 3 the code:

class Foo(object):
    pass

f = Foo()
f.__call__ = lambda *args : args

f(1, 2, 3)

returns as error Foo object is not callable. Why does that happen?

PS: With old-style classes it works as expected.

PPS: This behavior is intended (see accepted answer). As a work-around it's possible to define a __call__ at class level that just forwards to another member and set this "normal" member to a per-instance __call__ implementation.

like image 619
6502 Avatar asked Nov 20 '15 10:11

6502


1 Answers

Double-underscore methods are always looked up on the class, never the instance. See Special method lookup for new-style classes:

For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

That's because the type might need to support the same operation (in which case the special method is looked up on the metatype).

For example, classes are callable (that's how you produce an instance), but if Python looked up the __call__ method on the actual object, then you could never do so on classes that implement __call__ for their instances. ClassObject() would become ClassObject.__call__() which would fail because the self parameter is not passed in to the unbound method. So instead type(ClassObject).__call__(ClassObject) is used, and calling instance() translates to type(instance).__call__(instance).

To work around this, you could add a __call__ method to the class which checks for a __call__ attribute on the class, and if there, calls it.

like image 122
Martijn Pieters Avatar answered Nov 02 '22 19:11

Martijn Pieters