[Python-ideas] Fix that broken callable builtin
Steven D'Aprano
steve at pearwood.info
Sat Apr 18 19:38:57 CEST 2015
On Sat, Apr 18, 2015 at 02:41:29PM +0300, Ionel Cristian Mărieș wrote:
> On Sat, Apr 18, 2015 at 2:23 PM, Steven D'Aprano <steve at pearwood.info>
> wrote:
>
> > Are you sure this doesn't already work? It works for me in Python 3.3:
>
>
> It doesn't really work because of the incomplete checks done in
> `callable_builtin`. This is what I've tried:
>
> >>> class DynamicCallable:
> > ... is_callable = True
> > ...
> > ... def __init__(self, target):
> > ... self.target = target
> > ...
> > ... @property
> > ... def __call__(self):
> > ... if self.is_callable:
> > ... return self.target
> > ... else:
> > ... raise AttributeError("Not really ...")
> > ...
> > >>> dc = DynamicCallable(print)
> > >>> dc(1, 2, 3)
> > 1 2 3
> > >>> callable(dc)
> > True
> > >>> dc.is_callable = False
> > >>> callable(dc)
> > True ###### This should be False :(
Why do you think that it should be false? If you actually call dc, the
__call__ method/function/property (whatever you want to name it) gets
called, and it raises an exception just like you programmed it to. The
traceback clearly shows __call__ in the stack.
hasattr(type(dc), '__call__') returns true, therefore dc is callable. I
think that the fact that the __call__ method ends up raising an
AttributeError is irrelevant -- lots of callables raise AttributeError:
def func(): return None.spam
However, I think *this* is a bug in callable:
py> class Callable:
... def __getattribute__(self, name):
... if name == '__call__':
... raise AttributeError # Oh the lies we tell.
... return super().__getattribute__(name)
... def __call__(self):
... return 23
...
py> x = Callable()
py> callable(x)
False
py> x()
23
Clearly x is callable, since I just called it and it returned a value,
but callable() thinks it is not. Here is another example:
py> class X: pass
...
py> x = X()
py> x.__call__ = 42
py> callable(x)
True
py> x()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'X' object is not callable
I think callable() is badly broken. (Or at least *was* broken in 3.3
-- I don't have 3.4 handy to try it.)
--
Steve
More information about the Python-ideas
mailing list