Hello,
I had an issue today with the `callable` builtin because it doesn't correctly check that the object has the __call__ attribute.
Effectively what `callable(a)` does is `hasattr(type(a), '__call__')` but that's not very straightforward. A more straightforward implementation would do something like `hasattr(a, '__call__')`.
For example:
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> callable
<built-in function callable>
>>> class A:
... @property
... def __call__(self):
... raise AttributeError('go away')
...
>>> a = A()
>>> a
<__main__.A object at 0x000000000365B5C0>
>>> a.__call__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __call__
AttributeError: go away
>>> callable(a)
True
>>> # it should be False :(
So it boils down to this:
>>> hasattr(a, "__call__")
False
>>> hasattr(type(a), "__call__")
True
My issue is that I didn't call `callable(type(a))` but just `callable(a)`. Clearly mismatching what happens when you do hasattr(a, "__call__").
To put in contrast, this is legal and clearly indicates the descriptors are being used as expected:
>>> class B:
... @property
... def __call__(self):
... return lambda: 1
...
>>> b = B()
>>> b()
1
There
where I get slightly angry, sorry.
So were is this change actually useful? Proxies! Since new-style objects in Python you cannot really proxy the callable aspect of objects, because `callable` just checks that a field is set in a C struct.
This is fairly inconvenient because you have to know upfront if your target is going to be callable or not.
Thanks,
-- Ionel Cristian Mărieș, http://blog.ionelmc.ro