Search code examples
pythonpython-2.7python-3.xstatic-methodscallable

Python static method is not always callable


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'>

Solution

  • 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'>