Callable or not callable, that is the question!

U

Ulrich Eckhardt

Hello!

I just stumbled over a case where Python (2.7 and 3.3 on MS Windows)
fail to detect that an object is a function, using the callable()
builtin function. Investigating, I found out that the object was indeed
not callable, but in a way that was very unexpected to me:

class X:
@staticmethod
def example():
pass
test1 = example
test2 = [example,]

X.example() # OK
X.test1() # OK
X.test2[0]() # TypeError: 'staticmethod' object is not callable


Bug or feature?


Thanks!

Uli
 
P

Peter Otten

Ulrich said:
Hello!

I just stumbled over a case where Python (2.7 and 3.3 on MS Windows)
fail to detect that an object is a function, using the callable()
builtin function. Investigating, I found out that the object was indeed
not callable, but in a way that was very unexpected to me:

class X:
@staticmethod
def example():
pass
test1 = example
test2 = [example,]

X.example() # OK
X.test1() # OK
X.test2[0]() # TypeError: 'staticmethod' object is not callable

Slightly modified example:
.... def example(): return 42
........ example = example
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable

When you access an attribute its __get__() method is implicitly called. This
is part of the descriptor protocol:
<function example at 0x7fe45c0ea3b0>

While it would be easy to make staticmethod callable
.... def __call__(self, *args, **kw):
.... return self.__func__(*args, **kw)
........ def foo(): return "bar"
....'bar'

I see no clear advantage over the current situation.
Bug or feature?

No bug. Missing feature if you come up with a convincing use-case.
 
S

Steven D'Aprano

Hello!

I just stumbled over a case where Python (2.7 and 3.3 on MS Windows)
fail to detect that an object is a function, using the callable()
builtin function. Investigating, I found out that the object was indeed
not callable, but in a way that was very unexpected to me: [...]
X.test2[0]() # TypeError: 'staticmethod' object is not callable


Bug or feature?

In my opinion, a bug. I thought I had actually submitted it to the bug
tracker, but apparently I was a shameful slacker and did not. However
there was a discussion in this thread:

http://mail.python.org/pipermail/python-dev/2011-March/109090.html


Here's a simpler demonstration of the issue:

assert callable(staticmethod(lambda: None))
 
U

Ulrich Eckhardt

Am 11.07.2013 16:11, schrieb Peter Otten:
No bug. Missing feature if you come up with a convincing use-case.

class Parser:
def _handle_bool(input):
# ...
pass

types = {'bool': _handle_bool,
'boolean': _handle_bool,}

def parse(self, line):
t,s,v = line.partition(':')
handler = types[t]
return handler(v)

I want a utility function that really behaves just like a function. I'd
prefer to nest it inside the class that uses it, to make the code easier
to understand. Since I don't want the implicit self-binding either, I
would use staticmethod to make this clear, too.

Since I can live without any of this, it's not a big issue. What is to
me a big issue though is the fact that Python behaves unexpectedly and
reading Steven's response and the link there, it seems I'm not alone.

Greetings!

Uli
 
I

Ian Kelly

Hello!

I just stumbled over a case where Python (2.7 and 3.3 on MS Windows)
fail to detect that an object is a function, using the callable()
builtin function. Investigating, I found out that the object was indeed
not callable, but in a way that was very unexpected to me: [...]
X.test2[0]() # TypeError: 'staticmethod' object is not callable


Bug or feature?

In my opinion, a bug. I thought I had actually submitted it to the bug
tracker, but apparently I was a shameful slacker and did not. However
there was a discussion in this thread:

http://mail.python.org/pipermail/python-dev/2011-March/109090.html


Here's a simpler demonstration of the issue:

assert callable(staticmethod(lambda: None))

If staticmethod is going to be callable then classmethod should be
callable also.
 
S

Steven D'Aprano

To be a convincing use-case you would have to show a situation where
something had to be both a static method and a utility method rather
than just one or the other and also where you couldn't just have both.

I have a class where I have a function that needs to be called both while
the class is being constructed, and afterwards:

class Example:
@staticmethod
def do_stuff(arg):
...

do_stuff(23) # This doesn't work.

Example.do_stuff(42)


I have work-arounds: inside the class, I call do_stuff.__func__ instead
of do_stuff, but really, that's ugly and distracting and merely a work-
around for the fact that staticmethods aren't callable. To make them
callable is trivially easy: they just need a __call__ method that calls
__func__ for you.


If you can persuade me that you need _handle_bool as both a static
method and a utility function, you probably also need to explain why you
can't just use both:

class Parser:
def _handle_bool(input): ...
handle_bool = staticmethod(_handle_bool)

That's extremely inelegant. Why have two functions for something which is
conceptually one?
 
P

Peter Otten

Steven said:
I have a class where I have a function that needs to be called both while
the class is being constructed, and afterwards:

class Example:
@staticmethod
def do_stuff(arg):
...

do_stuff(23) # This doesn't work.

Example.do_stuff(42)

That is a bit too abstract for my taste to count as a use-case.

Also, as given the example will work in Python 3 when you remove the
@staticmethod ;)

That said I can see that the error comes as a surprise and I would be fine
with callable staticmethod objects.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top