In new style classes (default 3.x
) and inherited from object in 2.x
the attribute interception methods __getattr__
and __getattribute__
are no longer called for built-in operations on dunder overloaded methods of instances and instead the search begins at the class.
The rationale behind this, involves a technicallity introduced by the presence of MetaClasses.
Because classes are instances of metaclasses and because metaclasses might define certain operations that act on classes, skipping the class (which in this case can be considered an instance
) makes sense; you need to invoke the method defined in the metaclass which is defined to process classes (cls
as a first argument). If instance look-up was used, the look-up would use the class method which is defined for instances of that class (self
).
Another (disputed reason) involves optimization: Since built-in operations on instances are usually invoked very frequently, skipping the instance look-up altogether and going directly to the class saves us some time. [Source Lutz, Learning Python, 5th Edition]
The main area where this might be of inconvenience is when creating proxy objects that overload __getattr__
and __getattribute__
with intent to forward the calls to the embedded internal object. Since built-in invocation will skip the instance altogether they won't get caught and as an effect won't get forwarded to the embedded object.
An easy, but tedious, work-around for this is to actually overload all the dunders you wish to intercept in the proxy object. Then in these overloaded dunders you can delegate the call to the internal object as required.
The easiest work-around I can think of is setting the attribute on the class Foo
with setattr
:
setattr(Foo, '__call__', lambda *args: print(args))
f(1, 2, 3)
(<__main__.Foo object at 0x7f40640faa90>, 1, 2, 3)