12

While parsing attributes using __dict__, my @staticmethod is not callable.

Python 2.7.5 (default, Aug 29 2016, 10:12:21)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import (absolute_import, division, print_function)
>>> class C(object):
...   @staticmethod
...   def foo():
...     for name, val in C.__dict__.items():
...       if name[:2] != '__':
...          print(name, callable(val), type(val))
...
>>> C.foo()
foo  False  <type 'staticmethod'>
  • How is this possible?
  • How to check if a static method is callable?

I provide below a more detailed example:

Script test.py

from __future__ import (absolute_import, division, print_function)

class C(object):

  @staticmethod
  def foo():
    return 42

  def bar(self):
    print('Is bar() callable?', callable(C.bar))
    print('Is foo() callable?', callable(C.foo))
    for attribute, value in C.__dict__.items():
      if attribute[:2] != '__':
        print(attribute, '\t', callable(value), '\t', type(value))

c = C()
c.bar()

Result for python2

> python2.7 test.py
Is bar() callable? True
Is foo() callable? True
bar      True    <type 'function'>
foo      False   <type 'staticmethod'>

Same result for python3

> python3.4 test.py
Is bar() callable? True
Is foo() callable? True
bar      True    <class 'function'>
foo      False   <class 'staticmethod'>
oHo
  • 51,447
  • 27
  • 165
  • 200

2 Answers2

12

The reason for this behavior is the descriptor protocol. The C.foo won't return a staticmethod but a normal function while the 'foo' in __dict__ is a staticmethod (and staticmethod is a descriptor).

In short C.foo isn't the same as C.__dict__['foo'] in this case - but rather C.__dict__['foo'].__get__(C) (see also the section in the documentation of the Data model on descriptors):

>>> callable(C.__dict__['foo'].__get__(C))
True
>>> type(C.__dict__['foo'].__get__(C))
function

>>> callable(C.foo)
True
>>> type(C.foo)
function

>>> C.foo is C.__dict__['foo'].__get__(C)
True

In your case I would check for callables using getattr (which knows about descriptors and how to access them) instead of what is stored as value in the class __dict__:

def bar(self):
    print('Is bar() callable?', callable(C.bar))
    print('Is foo() callable?', callable(C.foo))
    for attribute in C.__dict__.keys():
        if attribute[:2] != '__':
            value = getattr(C, attribute)
            print(attribute, '\t', callable(value), '\t', type(value))

Which prints (on python-3.x):

Is bar() callable? True
Is foo() callable? True
bar      True    <class 'function'>
foo      True    <class 'function'>

The types are different on python-2.x but the result of callable is the same:

Is bar() callable? True
Is foo() callable? True
bar      True    <type 'instancemethod'>
foo      True    <type 'function'>
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • Thanks for your explanation. What should I change in my first snippet in order to known that `foo` is `callable`? Should I use `print(name, callable(val.__get__(C)))`? Or should I use something like `if type(name) == types.StaticmethodType`? Please advice a fix ;-) Cheers – oHo Jul 28 '17 at 14:41
  • Ah, I knew I forgot something. I updated the answer :) – MSeifert Jul 28 '17 at 14:44
  • Yep `getattr(C, attribute)` does the job and I see that your `type(value)` returns now `` instead of `` in my code. – oHo Jul 28 '17 at 14:51
4

You can't check if a staticmethod object is callable or not. This was discussed on the tracker in Issue 20309 -- Not all method descriptors are callable and closed as "not a bug".

In short, there's been no rationale for implementing __call__ for staticmethod objects. The built-in callable has no way to know that the staticmethod object is something that essentially "holds" a callable.

Though you could implement it (for staticmethods and classmethods), it would be a maintenance burden that, as previously mentioned, has no real motivating use-cases.


For your case, you can use getattr(C, name) to perform a look-up for the object named name; this is equivalent to performing C.<name>. getattr, after finding the staticmethod object, will invoke its __get__ to get back the callable it's managing. You can then use callable on that.

A nice primer on descriptors can be found in the docs, take a look at Descriptor HOWTO.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • Thanks for your excellent rational. I request the same as for MSeifert: What should I change in my first snippet in order to known that `foo` is `callable`? Should I use `print(name, callable(val.__get__(C)))`? Or should I use something like `if type(name) == types.StaticmethodType`? Please advice a fix ;-) Cheers – oHo Jul 28 '17 at 14:43
  • @olibre `callable(getattr(C, name))` is one option, it is always better than grabbing dunders (`__get__`) – Dimitris Fasarakis Hilliard Jul 28 '17 at 14:45
  • I have just successfully tested `getattr(C, attribute)`. You may write your comment in your answer. I see that `type(getattr(C, attribute))` returns `` instead of `` in my code. You may also explain it within your answer. Cheers – oHo Jul 28 '17 at 14:56
  • 3
    `getattr` is definitely the indisputably right option. The entire problem is that `obj.attr` does a bit more than just `obj.__dict__['attr']`, and just blindly trying to do `obj.__class__.__dict__['attr'].__get__(obj.__class__)` isn't right either because it ignores instance attributes, mro and non-descriptor attributes. `getattr` knows about all the complexities and does the right thing. – lvc Jul 28 '17 at 15:00
  • 1
    @olibre That's good. Both those objects (depending on Python version) are callable. Using `getattr` you've invoked the descriptor protocol (calling `staticmethod_obj.__get__`) and get back the callable it wraps. Take a look at the tutorial on [descriptors](https://docs.python.org/2.7/howto/descriptor.html#static-methods-and-class-methods) for a nice overview. – Dimitris Fasarakis Hilliard Jul 28 '17 at 15:05