Python static method cannot always be called

When parsing attributes using __dict__ my @staticmethod 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 allowed?



The following is 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'> 
+7
python static-methods callable
Jul 28 '17 at 14:25
source share
2 answers

The reason for this behavior is the descriptor protocol. C.foo does not return a staticmethod , but a normal function, and the 'foo' in __dict__ is a staticmethod (and staticmethod is a handle).

In short, C.foo in this case does not match C.__dict__['foo'] , but rather C.__dict__['foo'].__get__(C) (see also the section in the Data Model for Descriptors documentation):

 >>> 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 the use of called objects using getattr (which knows about descriptors and how to access them) instead of what is stored as a value in the __dict__ class:

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

What prints (in python-3.x):

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

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

 Is bar() callable? True Is foo() callable? True bar True <type 'instancemethod'> foo True <type 'function'> 
+9
Jul 28 '17 at 14:30
source share

You cannot check if the staticmethod object staticmethod callable or not. This was discussed on the tracker in Issue 20309 - Not all method descriptors are called and closed as "not an error."

In short, there was no justification for implementing __call__ for staticmethod objects. The built-in callable does not know that the staticmethod object is what essentially “holds” the callee.

Although you could implement it (for staticmethod and classmethod s), this would be a service burden, which, as already mentioned, has no real motive for using it.




In your case, you can use getattr(C, name) to search for an object named name ; this is equivalent to doing C.<name> . getattr , after finding the staticmethod object, will output it __get__ to return the control it __get__ . Then you can use callable .

A good primer in descriptors can be found in the docs, see Descriptor HOWTO .

+3
Jul 28 '17 at 14:37
source share



All Articles